//
// ObjectiveCC.c
// Objective CC
//
// Created by Chris Galzerano on 1/22/25.
//
#include "ObjectiveCC.h"
//C Standard Library
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <limits.h>
#include <unistd.h>
#include <math.h>
#include <setjmp.h>
#include <ctype.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <libgen.h>
#include <regex.h>
#include <pthread.h>
#include <sys/termios.h>
#include <fcntl.h>
#include <sys/select.h>
#include <dirent.h>
#include <cJSON.h>
//Graphics Library
#include "CPUGraphics.h"
#include "esp_heap_caps.h"
CCRange* ccRange(UInteger location, UInteger length) {
CCRange* range = (CCRange*)cc_safe_alloc(1, sizeof(CCRange));
range->type = CCType_Range;
if (!range) return NULL;
range->loc = location;
range->len = length;
return range;
}
CCPoint* ccPoint(float x, float y) {
CCPoint* point = (CCPoint*)cc_safe_alloc(1, sizeof(CCPoint));
point->type = CCType_Point;
if (!point) return NULL;
point->x = x;
point->y = y;
return point;
}
CCSize* ccSize(float width, float height) {
CCSize* size = (CCSize*)cc_safe_alloc(1, sizeof(CCSize));
size->type = CCType_Size;
if (!size) return NULL;
size->width = width;
size->height = height;
return size;
}
CCRect* ccRect(float x, float y, float width, float height) {
CCRect* rect = (CCRect*)cc_safe_alloc(1, sizeof(CCRect));
rect->type = CCType_Rect;
if (!rect) return NULL;
rect->origin = ccPoint(x, y);
rect->size = ccSize(width, height);
return rect;
}
bool rectContainsPoint(CCRect* rect, CCPoint* point) {
if ((point->x >= rect->origin->x) &&
(point->y >= rect->origin->y) &&
(point->x <= rect->origin->x+rect->size->width) &&
(point->y <= rect->origin->y+rect->size->height)) {
return true;
}
return false;
}
bool rectContainsRect(CCRect* rect, CCRect* rect1) {
//Is rect1 x origin >= rect x origin AND
//Is rect1 x origin + rect1 width <= rect x origin + rect width AND
//Is rect1 y origin >= rect y origin AND
//Is rect1 y origin + rect1 width <= rect y origin + rect height
if ((rect1->origin->x >= rect->origin->x) &&
(rect1->origin->x + rect1->size->width <= rect->origin->x + rect->size->width) &&
(rect1->origin->y >= rect->origin->y) &&
(rect1->origin->y + rect1->size->height <= rect->origin->y + rect->size->height)) {
return true;
}
return false;
}
//Threading Functions
CCThread* ccThread(void) {
CCThread* newThread = (CCThread*)cc_safe_alloc(1, sizeof(CCThread));
if (!newThread) return NULL;
newThread->type = CCType_Thread;
newThread->threadType = CCThreadBackground;
return newThread;
}
CCThread* ccThreadWithType(CCThreadType threadType) {
CCThread* newThread = (CCThread*)cc_safe_alloc(1, sizeof(CCThread));
if (!newThread) return NULL;
newThread->type = CCType_Thread;
newThread->threadType = threadType;
return newThread;
}
CCThread* ccThreadWithParameters(CCThreadType threadType, CCThreadFunction function, CCThreadCompletionCallback callback, void* functionArg, void* callbackArg) {
CCThread* newThread = (CCThread*)cc_safe_alloc(1, sizeof(CCThread));
if (!newThread) return NULL;
newThread->type = CCType_Thread;
newThread->threadType = threadType;
newThread->function = function;
newThread->callback = callback;
newThread->callbackArg = callbackArg;
newThread->functionArg = functionArg;
return newThread;
}
void* threadWrapper(void* arg) {
CCThread* task = (CCThread*)arg;
task->function(task->functionArg);
if (task->callback) {
task->callback(task->callbackArg);
}
free(task);
return NULL;
}
void threadExecuteWithCallback(CCThreadFunction task, void* taskArg, CCThreadType mode, CCThreadCompletionCallback callback, void* callbackArg) {
if (mode == CCThreadBackground) {
pthread_t thread;
CCThread* t = (CCThread*)cc_safe_alloc(1, sizeof(CCThread));
t->function = task;
t->functionArg = taskArg;
t->callback = callback;
t->callbackArg = callbackArg;
int result = pthread_create(&thread, NULL, threadWrapper, t);
if (result != 0) {
fprintf(stderr, "Error creating background thread\n");
free(t);
} else {
pthread_detach(thread);
}
} else if (mode == CCThreadMain) {
task(taskArg);
if (callback) {
callback(callbackArg);
}
}
}
void threadExecute(CCThread* thread) {
threadExecuteWithCallback(thread->function, thread->functionArg, thread->threadType, thread->callback, thread->callbackArg);
}
//Serial Port Functions
/*CCArray* serialPortList(void) {
CCArray *result = array();
const char *port_prefixes[] = {
"/dev/ttyUSB", // USB-serial devices on Linux
"/dev/ttyACM", // ACM serial devices on Linux
"/dev/ttyS", // Standard serial ports on Linux
"/dev/cu.usb", // USB-serial devices on macOS
"/dev/tty.usb" // USB-serial devices on macOS
};
const int prefix_count = sizeof(port_prefixes) / sizeof(port_prefixes[0]);
DIR *dev_dir = opendir("/dev");
if (dev_dir == NULL) {
perror("opendir");
return NULL;
}
struct dirent *entry;
while ((entry = readdir(dev_dir)) != NULL) {
for (int i = 0; i < prefix_count; ++i) {
if (strncmp(entry->d_name, port_prefixes[i] + strlen("/dev/"), strlen(port_prefixes[i]) - strlen("/dev/")) == 0) {
char full_path[256];
snprintf(full_path, sizeof(full_path), "/dev/%s", entry->d_name);
CCString *portString = stringWithCString(full_path);
arrayAddObject(result, portString);
}
}
}
closedir(dev_dir);
return result;
}*/
int open_serial_port(const char *device, speed_t baudrate) {
int fd = open(device, O_RDWR | O_NOCTTY | O_SYNC);
if (fd < 0) {
perror("open");
return -1;
}
struct termios options;
if (tcgetattr(fd, &options) != 0) {
perror("tcgetattr");
close(fd);
return -1;
}
cfsetispeed(&options, baudrate);
cfsetospeed(&options, baudrate);
options.c_cflag = (options.c_cflag & ~CSIZE) | CS8;
options.c_iflag &= ~IGNBRK;
options.c_lflag = 0;
options.c_oflag = 0;
options.c_cc[VMIN] = 1;
options.c_cc[VTIME] = 1;
options.c_iflag &= ~(IXON | IXOFF | IXANY);
options.c_cflag |= (CLOCAL | CREAD);
options.c_cflag &= ~(PARENB | PARODD);
options.c_cflag &= ~CSTOPB;
//options.c_cflag &= ~CRTSCTS;
if (tcsetattr(fd, TCSANOW, &options) != 0) {
perror("tcsetattr");
close(fd);
return -1;
}
return fd;
}
// Function to monitor the serial port and invoke the callback when data is available
void listen_serial_port(bool *listening, int fd, CCSerialPortCallback callback) {
char buffer[256];
fd_set readfds;
if (listening == false) {
printf("serial port remove listener");
}
while (*listening) {
FD_ZERO(&readfds);
FD_SET(fd, &readfds);
int max_fd = fd + 1;
// Wait for data on the serial port
int result = select(max_fd, &readfds, NULL, NULL, NULL);
if (result > 0 && FD_ISSET(fd, &readfds)) {
ssize_t n = read(fd, buffer, sizeof(buffer) - 1);
if (n > 0) {
buffer[n] = '\0';
callback(buffer); // Call the callback function with the data
} else if (n < 0) {
perror("read");
break;
}
} else if (result < 0) {
perror("select");
break;
}
}
}
void close_serial_port(int fd) {
close(fd);
}
void write_to_serial_port(int fd, const char *message) {
size_t len = strlen(message);
ssize_t n = write(fd, message, len);
if (n != len) {
perror("write");
}
}
char* read_from_serial_port(int fd) {
char buffer[256];
ssize_t n = read(fd, buffer, sizeof(buffer) - 1);
if (n > 0) {
buffer[n] = '\0';
char *result = strdup(buffer);
return result;
} else if (n < 0) {
perror("read");
}
return NULL;
}
CCSerialPort* serialPort(void) {
CCSerialPort* newSerialPort = (CCSerialPort*)cc_safe_alloc(1, sizeof(CCSerialPort));
if (!newSerialPort) return NULL;
newSerialPort->type = CCType_SerialPort;
return newSerialPort;
}
CCSerialPort* serialPortWithName(CCString* name) {
CCSerialPort* newSerialPort = (CCSerialPort*)cc_safe_alloc(1, sizeof(CCSerialPort));
if (!newSerialPort) return NULL;
newSerialPort->type = CCType_SerialPort;
newSerialPort->name = name;
return newSerialPort;
}
int serialPortOpen(CCSerialPort* serialPort) {
int fd = open_serial_port(cStringOfString(serialPort->name), serialPort->baudRate);
serialPort->fd = fd;
return fd;
}
void serialPortClose(CCSerialPort* serialPort) {
serialPortRemoveListener(serialPort);
close_serial_port(serialPort->fd);
}
void serialPortAddListener(CCSerialPort* serialPort) {
listen_serial_port(&serialPort->listening, serialPort->fd, serialPort->callback);
}
void serialPortRemoveListener(CCSerialPort* serialPort) {
*(&serialPort->listening) = false;
}
void serialPortSendData(CCSerialPort* serialPort, CCString* data) {
write_to_serial_port(serialPort->fd, cStringOfString(data));
}
//Graphics Functions
//Should redraw all graphics of program?
static bool shouldUpdate = true;
// Create a new CCColor
CCColor* color(Float r, Float g, Float b, Float a) {
CCColor* newColor = (CCColor*)cc_safe_alloc(1, sizeof(CCColor));
newColor->type = CCType_Color;
if (!newColor) return NULL;
newColor->r = r;
newColor->g = g;
newColor->b = b;
newColor->a = a;
return newColor;
}
// Helper to convert CCColor (0.0-1.0) to ColorRGBA (0-255)
// --- Helper: Convert High-Level CCColor (float) to Low-Level ColorRGBA (byte) ---
ColorRGBA convert_cc_color(CCColor* cc) {
ColorRGBA c;
if (cc) {
// Clamp and convert 0.0-1.0 float to 0-255 byte
c.r = (uint8_t)(cc->r * 255.0f);
c.g = (uint8_t)(cc->g * 255.0f);
c.b = (uint8_t)(cc->b * 255.0f);
c.a = (uint8_t)(cc->a * 255.0f);
} else {
// Default to transparent if null
c.r = 0; c.g = 0; c.b = 0; c.a = 0;
}
return c;
}
CCColor* convert_colorrgba_to_cccolor(ColorRGBA c) {
CCColor* cc = (CCColor*)cc_safe_alloc(1, sizeof(CCColor));
cc->type = CCType_Color;
if (!cc) return NULL;
// Convert 0-255 byte back to 0.0-1.0 float
cc->r = (Float)(c.r / 255.0f);
cc->g = (Float)(c.g / 255.0f);
cc->b = (Float)(c.b / 255.0f);
cc->a = (Float)(c.a / 255.0f);
cc->type = CCType_Color;
return cc;
}
// Constructor for high-level CCGradient
CCGradient* gradientWithColors(CCArray* colors, CCArray* locations, Float angle) {
CCGradient* newGrad = (CCGradient*)cc_safe_alloc(1, sizeof(CCGradient));
newGrad->type = CCType_Gradient; // You might need to add this to your CCType enum
newGrad->colors = colors;
newGrad->locations = locations;
newGrad->angle = angle;
newGrad->gradType = GRADIENT_TYPE_LINEAR;
return newGrad;
}
// Helper to allocate a low-level Gradient from a high-level CCGradient
// NOTE: Returns a pointer that must be freed by the graphics task!
Gradient* create_low_level_gradient(CCGradient* ccGrad) {
if (!ccGrad || !ccGrad->colors || !ccGrad->locations) return NULL;
int count = ccGrad->colors->count;
Gradient* lowLevelGrad = (Gradient*)cc_safe_alloc(1, sizeof(Gradient));
lowLevelGrad->stops = (ColorStop*)cc_safe_alloc(1, sizeof(ColorStop) * count);
lowLevelGrad->numStops = count;
lowLevelGrad->angle = ccGrad->angle;
// --- FIX: Copy the type ---
lowLevelGrad->type = ccGrad->gradType;
// --------------------------
// Convert data
for (int i = 0; i < count; i++) {
CCColor* ccCol = (CCColor*)arrayObjectAtIndex(ccGrad->colors, i);
CCNumber* ccLoc = (CCNumber*)arrayObjectAtIndex(ccGrad->locations, i);
lowLevelGrad->stops[i].color = convert_cc_color(ccCol);
lowLevelGrad->stops[i].position = (float)ccLoc->doubleValue;
}
return lowLevelGrad;
}
// Create a new CCLayer with default properties
CCLayer* layer(void) {
CCLayer* newLayer = (CCLayer*)cc_safe_alloc(1, sizeof(CCLayer));
newLayer->type = CCType_Layer;
if (!newLayer) return NULL;
newLayer->frame = ccRect(0, 0, 0, 0);
newLayer->backgroundColor = color(1, 1, 1, 0);
newLayer->gradient = NULL;
newLayer->cornerRadius = 0.0;
newLayer->borderColor = color(0, 0, 0, 1); // Black border
newLayer->borderWidth = 0.0;
newLayer->shadowOffset = ccPoint(0, 0);
newLayer->shadowRadius = 0.0;
newLayer->shadowOpacity = 0.0;
newLayer->shadowColor = color(0, 0, 0, 0); // Transparent shadow
newLayer->superlayer = NULL;
newLayer->sublayers = array(); // Empty initially
return newLayer;
}
// Create a new CCLayer with a specified frame
CCLayer* layerWithFrame(CCRect* frame) {
CCLayer* newLayer = layer();
if (!newLayer) return NULL;
newLayer->frame = frame;
return newLayer;
}
// Create a new CCView with default properties
CCView* view(void) {
CCView* newView = (CCView*)cc_safe_alloc(1, sizeof(CCView));
newView->type = CCType_View;
if (!newView) return NULL;
newView->frame = ccRect(0, 0, 0, 0);
newView->backgroundColor = color(1, 1, 1, 0);
newView->layer = layer(); // Use existing layer() function to initialize the layer
if (!newView->layer) {
free(newView);
return NULL;
}
newView->shapeLayer = NULL;
newView->superview = NULL;
newView->subviews = array(); // Empty initially
newView->gestureRecognizers = array();
return newView;
}
// Create a new CCView with a specified frame
CCView* viewWithFrame(CCRect* frame) {
CCView* newView = (CCView*)cc_safe_alloc(1, sizeof(CCView));
newView->type = CCType_View;
if (!newView) return NULL;
newView->frame = frame;
newView->backgroundColor = color(1, 1, 1, 0);
//newView->layer = (CCLayer*)cc_safe_alloc(1, sizeof(CCLayer)); // Initialize the layer with the specified frame
newView->layer = layerWithFrame(frame);
newView->frame = frame;
if (!newView->layer) {
free(newView);
return NULL;
}
newView->shapeLayer = NULL;
newView->superview = NULL;
newView->subviews = array(); // Empty initially
newView->gestureRecognizers = array();
return newView;
}
/*void viewAddSubview(void* view, void* subview) {
CCView* view1 = view;
arrayAddObject(view1->subviews, subview);
shouldUpdate = true;
}*/
void viewRemoveFromSuperview(void* child) {
if (!child) return;
// 1. Unwrap the CHILD to find the underlying CCView struct
CCType childType = *((CCType*)child);
CCView* childBase = NULL;
if (childType == CCType_View) {
childBase = (CCView*)child;
}
else if (childType == CCType_ImageView) {
childBase = ((CCImageView*)child)->view;
}
else if (childType == CCType_Label) {
childBase = ((CCLabel*)child)->view;
}
else if (childType == CCType_ScrollView) {
childBase = ((CCScrollView*)child)->view;
}
// 2. Remove if linked
if (childBase && childBase->superview) {
// FIX: Cast struct CCView* to CCView* to satisfy the compiler
CCView* parent = (CCView*)childBase->superview;
// Remove the Wrapper Object (the original 'child' void*) from parent's list
if (parent->subviews) {
arrayRemoveObject(parent->subviews, child);
}
// Clear the link
childBase->superview = NULL;
}
}
void viewAddSubview(void* parent, void* child) {
if (!parent || !child) return;
// 1. Unwrap the PARENT (The container)
// We assume the parent passed in is usually a CCView (like mainWindowView or container),
// but safety checking the type is good practice.
CCView* parentView = (CCView*)parent;
// 2. Unwrap the CHILD (The item being added)
// We need to find the actual CCView struct inside the wrapper to set its 'superview'.
CCType childType = *((CCType*)child);
CCView* childBase = NULL;
if (childType == CCType_View) {
childBase = (CCView*)child;
}
else if (childType == CCType_ImageView) {
childBase = ((CCImageView*)child)->view;
}
else if (childType == CCType_Label) {
childBase = ((CCLabel*)child)->view;
}
else if (childType == CCType_ScrollView) {
childBase = ((CCScrollView*)child)->view;
}
else if (childType == CCType_FramebufferView) {
childBase = ((CCFramebufferView*)child)->view;
}
// 3. Perform the Linking
if (parentView && childBase) {
// A. Add to Parent's list (Visual hierarchy)
arrayAddObject(parentView->subviews, child);
// B. Link Child to Parent (Logic hierarchy) -> THIS WAS MISSING
childBase->superview = (struct CCView*)parentView;
}
shouldUpdate = true;
}
//If a CCLabel exists or a CCImageView exists, it is not a class
//that inherits or is subclassed from CCView, it is its own struct
//that contains a CCView member.
//
//A CCImageView would also be its own struct that has a CCView member
//
//If a CCView is the root concept of the view heiarchy, when adding
//subviews that are a more complex type than CCView, such as CCLabel
//or CCImageView structs, the subviews member of CCView is a CCArray
//The CCArray allows to hold any type of struct, but there is no way
//to know what type of struct it is...
//
//Now, each view has a type parameter that can be checked when
//iterating through the array
void viewSetBackgroundColor(void* view, CCColor* color) {
CCView* view1 = view;
view1->layer->backgroundColor = color;
shouldUpdate = true;
}
void layerSetCornerRadius(CCLayer* layer, Float cornerRadius) {
layer->cornerRadius = cornerRadius;
shouldUpdate = true;
}
void viewSetFrame(void* view, CCRect* frame) {
CCView* view1 = view;
view1->frame = frame;
view1->layer->frame = frame;
shouldUpdate = true;
}
void viewAddGestureRecognizer(void* view, CCGestureRecognizer* gestureRecognizer) {
CCView* view1 = view;
if (view1->type == CCType_Label) {
view1 = ((CCLabel*)view)->view;
}
else if (view1->type == CCType_ImageView) {
view1 = ((CCImageView*)view)->view;
}
gestureRecognizer->view = view1;
arrayAddObject(view1->gestureRecognizers, gestureRecognizer);
}
// Clean up function for CCView
void freeCCView(CCView* view) {
if (view) {
if (view->layer) free(view->layer);
//if (view->subviews) freeCCArray(view->subviews);
free(view);
}
}
void freeCCGradient(CCGradient* gradient) {
// 1. Safety check
if (gradient == NULL) {
return;
}
// 2. Free the Colors Array
// This assumes freeCCArray handles freeing the array struct
// AND the objects inside it (CCColor objects).
if (gradient->colors != NULL) {
freeCCArray(gradient->colors);
gradient->colors = NULL; // Safety: prevent dangling pointer
}
// 3. Free the Locations Array
// This assumes freeCCArray handles freeing the CCNumber objects inside.
if (gradient->locations != NULL) {
freeCCArray(gradient->locations);
gradient->locations = NULL;
}
// 4. Free the Gradient Struct itself
free(gradient);
}
void freeViewHierarchy(CCView* view) {
if (!view) return;
// 1. SAFETY: Detach from Parent (Critical for Sub-tree deletion)
// If you free a child but the parent is still alive, the parent
// holds a dangling pointer and will crash if it tries to draw.
// (Assuming you have a function to remove it from the parent's array)
// if (view->superview) viewRemoveSubview(view->superview, view);
// 2. Recursively free subviews
if (view->subviews) {
for (int i = 0; i < view->subviews->count; i++) {
void* childObj = arrayObjectAtIndex(view->subviews, i);
CCType type = *((CCType*)childObj);
if (type == CCType_View) {
freeViewHierarchy((CCView*)childObj);
}
else if (type == CCType_Label) {
CCLabel* lbl = (CCLabel*)childObj;
freeViewHierarchy(lbl->view); // Free inner view
if (lbl->text) freeCCString(lbl->text);
if (lbl->textColor) free(lbl->textColor); // <-- LEAK FIX
free(lbl);
}
else if (type == CCType_ImageView) {
CCImageView* img = (CCImageView*)childObj;
freeViewHierarchy(img->view);
// Only free the image if you own it (not cached)
// if (img->image) freeCCImage(img->image);
free(img);
}
else if (type == CCType_ScrollView) {
CCScrollView* sv = (CCScrollView*)childObj;
freeViewHierarchy(sv->view);
free(sv);
}
// Add Button/Switch cases here if you wrap them differently!
}
freeCCArray(view->subviews);
}
// 3. Free Layer & its properties
if (view->layer) {
if (view->layer->gradient) freeCCGradient(view->layer->gradient);
// --- LEAK FIXES ---
if (view->layer->borderColor) free(view->layer->borderColor);
if (view->layer->shadowColor) free(view->layer->shadowColor);
// ------------------
free(view->layer);
}
// 4. Free View Properties
if (view->frame) freeCCRect(view->frame);
// --- LEAK FIX ---
//if (view->backgroundColor) free(view->backgroundColor);
// ----------------
// 5. Finally, free the struct
free(view);
}
CCGestureRecognizer* gestureRecognizerWithType(CCGestureType gestureType, CCGestureAction action) {
CCGestureRecognizer* newGestureRecognizer = (CCGestureRecognizer*)cc_safe_alloc(1, sizeof(CCGestureRecognizer));
newGestureRecognizer->type = CCType_GestureRecognizer;
if (!newGestureRecognizer) return NULL;
newGestureRecognizer->gestureType = gestureType;
newGestureRecognizer->action = action;
return newGestureRecognizer;
}
// Clean up function for CCColor
void freeCCColor(CCColor* color) {
free(color);
}
// Clean up function for CCColor
void freeCCRect(CCRect* rect) {
free(rect);
}
// Create a new CCFont
CCFont* font(CCString* filePath, Float renderingSize) {
CCFont* newFont = (CCFont*)cc_safe_alloc(1, sizeof(CCFont));
newFont->type = CCType_Font;
if (!newFont) return NULL;
newFont->filePath = filePath;
newFont->renderingSize = renderingSize;
//int loadedFont = LoadFont(filePath->string, (int)renderingSize);
//if (loadedFont != -1) {
// newFont->isLoaded = true;
//}
return newFont;
}
void labelSetDefault(CCLabel* newLabel) {
newLabel->textColor = color(0, 0, 0, 1.0);
newLabel->text = string();
newLabel->textAlignment = CCTextAlignmentLeft;
newLabel->textVerticalAlignment = CCTextVerticalAlignmentCenter;
newLabel->lineBreakMode = CCLineBreakWordWrapping;
newLabel->fontSize = 12.0f;
//newLabel->font = defaultFont;
}
CCLabel* label(void) {
CCLabel* newLabel = (CCLabel*)cc_safe_alloc(1, sizeof(CCLabel));
newLabel->type = CCType_Label;
if (!newLabel) return NULL;
newLabel->view = view();
if (!newLabel->view) {
free(newLabel);
return NULL;
}
labelSetDefault(newLabel);
return newLabel;
}
// Create a new CCView with a specified frame
CCLabel* labelWithFrame(CCRect* frame) {
CCLabel* newLabel = (CCLabel*)cc_safe_alloc(1, sizeof(CCLabel));
newLabel->type = CCType_Label;
if (!newLabel) return NULL;
newLabel->view = viewWithFrame(frame);
if (!newLabel->view) {
free(newLabel);
return NULL;
}
labelSetDefault(newLabel);
return newLabel;
}
void labelSetText(CCLabel* label, CCString* text) {
label->text = text;
shouldUpdate = true;
}
CCFramebufferView* framebufferView(void) {
CCFramebufferView* newFbView = (CCFramebufferView*)cc_safe_alloc(1, sizeof(CCFramebufferView));
if (!newFbView) return NULL;
newFbView->type = CCType_FramebufferView; // Make sure this is in your CCType enum!
newFbView->view = view();
if (!newFbView->view) {
free(newFbView);
return NULL;
}
newFbView->framebuffer = NULL;
return newFbView;
}
CCFramebufferView* framebufferViewWithFrame(CCRect* frame) {
CCFramebufferView* newFbView = (CCFramebufferView*)cc_safe_alloc(1, sizeof(CCFramebufferView));
if (!newFbView) return NULL;
newFbView->type = CCType_FramebufferView;
newFbView->view = viewWithFrame(frame);
if (!newFbView->view) {
free(newFbView);
return NULL;
}
newFbView->framebuffer = NULL;
return newFbView;
}
void framebufferViewSetFramebuffer(CCFramebufferView* fbView, Framebuffer* fb) {
if (!fbView) return;
fbView->framebuffer = fb;
shouldUpdate = true;
}
CCPointPath* pointPath(void) {
CCPointPath* newPointPath = (CCPointPath*)cc_safe_alloc(1, sizeof(CCPointPath));
newPointPath->type = CCType_PointPath;
if (!newPointPath) return NULL;
newPointPath->points = array();
if (!newPointPath->points) {
free(newPointPath);
return NULL;
}
return newPointPath;
}
CCPointPath* pointPathWithPoints(CCArray* points) {
CCPointPath* newPointPath = (CCPointPath*)cc_safe_alloc(1, sizeof(CCPointPath));
newPointPath->type = CCType_PointPath;
newPointPath->points = points;
if (!newPointPath) return NULL;
return newPointPath;
}
void pointPathAddPoint(CCPointPath* pointPath, CCPoint* point) {
if (pointPath && pointPath->points) {
arrayAddObject(pointPath->points, point);
}
}
CCShapeLayer* shapeLayer(void) {
CCShapeLayer* newShapeLayer = (CCShapeLayer*)cc_safe_alloc(1, sizeof(CCShapeLayer));
newShapeLayer->type = CCType_ShapeLayer;
newShapeLayer->shapeType = CCShapeTypePolygon;
if (!newShapeLayer) return NULL;
newShapeLayer->pointPath = pointPath();
if (!newShapeLayer->pointPath) {
free(newShapeLayer);
return NULL;
}
return newShapeLayer;
}
CCShapeLayer* shapeLayerWithPointPath(CCPointPath* pointPath) {
CCShapeLayer* newShapeLayer = (CCShapeLayer*)cc_safe_alloc(1, sizeof(CCShapeLayer));
newShapeLayer->type = CCType_ShapeLayer;
newShapeLayer->shapeType = CCShapeTypePolygon;
newShapeLayer->gradient = NULL;
if (!newShapeLayer) return NULL;
newShapeLayer->pointPath = pointPath;
return newShapeLayer;
}
// Clean up function for CCFont
void freeCCFont(CCFont* font) {
free(font);
}
// Clean up function for CCLabel
void freeCCLabel(CCLabel* label) {
if (label) {
if (label->view) freeCCView(label->view);
free(label);
}
}
CCImage* image(void) {
CCImage* newImage = (CCImage*)cc_safe_alloc(1, sizeof(CCImage));
newImage->type = CCType_Image;
if (!newImage) return NULL;
newImage->size = ccSize(0, 0);
return newImage;
}
CCImage* imageWithData(unsigned char* imageData, Integer width, Integer height) {
CCImage* newImage = (CCImage*)cc_safe_alloc(1, sizeof(CCImage));
newImage->type = CCType_Image;
if (!newImage) return NULL;
newImage->imageData = imageData;
newImage->size = ccSize(width, height);
return newImage;
}
CCImage* imageWithFile(CCString* filePath) {
CCImage* newImage = (CCImage*)cc_safe_alloc(1, sizeof(CCImage));
if (!newImage) return NULL;
newImage->type = CCType_Image;
// 1. Store the path. We will use this later in the graphics loop.
// We assume filePath is a valid CCString created via ccs() or similar.
newImage->filePath = filePath;
// 2. Initialize Data to NULL (Lazy Loading)
newImage->imageData = NULL;
// 3. Initialize Size to 0,0
// Since we haven't opened the file, we don't know the size yet.
// This is fine because CCImageView uses the VIEW'S frame to size the image.
newImage->size = ccSize(0, 0);
newImage->texture = 0; // Unused in this software renderer implementation
return newImage;
}
CCImageView* imageView(void) {
CCImageView* newImageView = (CCImageView*)cc_safe_alloc(1, sizeof(CCImageView));
newImageView->type = CCType_ImageView;
if (!newImageView) return NULL;
newImageView->alpha = 1.0;
newImageView->ignoreTouch = false;
newImageView->view = view();
if (!newImageView->view) {
free(newImageView);
return NULL;
}
return newImageView;
}
CCImageView* imageViewWithFrame(CCRect* frame) {
CCImageView* newImageView = (CCImageView*)cc_safe_alloc(1, sizeof(CCImageView));
newImageView->type = CCType_ImageView;
if (!newImageView) return NULL;
newImageView->alpha = 1.0;
newImageView->ignoreTouch = false;
newImageView->view = viewWithFrame(frame);
if (!newImageView->view) {
free(newImageView);
return NULL;
}
return newImageView;
}
void imageViewSetImage(CCImageView* imageView, CCImage* image) {
imageView->image = image;
shouldUpdate = true;
}
CCScrollView* scrollViewWithFrame(CCRect* frame) {
CCScrollView* sv = (CCScrollView*)cc_safe_alloc(1, sizeof(CCScrollView));
if (!sv) return NULL;
sv->type = CCType_ScrollView;
// 1. Create Viewport (The fixed window)
sv->view = viewWithFrame(frame);
sv->view->layer->masksToBounds = true; // CRITICAL: Clip children
sv->view->backgroundColor = color(0,0,0,0); // Default transparent
// 2. Create Content View (The moving sheet)
// Starts at 0,0 with same size as viewport (will grow later)
sv->contentView = viewWithFrame(ccRect(0, 0, frame->size->width, frame->size->height));
sv->contentView->backgroundColor = color(0,0,0,0);
// Add content to viewport
viewAddSubview(sv->view, sv->contentView);
// 3. Init properties
sv->contentSize = ccSize(frame->size->width, frame->size->height);
sv->contentOffset = ccPoint(0, 0);
return sv;
}
void scrollViewSetContentSize(CCScrollView* sv, CCSize* size) {
if (!sv) return;
// Update struct
sv->contentSize->width = size->width;
sv->contentSize->height = size->height;
// Update actual view frame
sv->contentView->frame->size->width = size->width;
sv->contentView->frame->size->height = size->height;
printf("scrollViewSetContentSize");
printf("sv->contentView->layer %d\n", (sv->contentView->layer == NULL)? 0 : 1);
printf("sv->contentView->layer->frame %d\n", (sv->contentView->layer->frame == NULL)? 0 : 1);
// Also update layer frame (needed for borders/bg)
sv->contentView->layer->frame->size->width = size->width;
sv->contentView->layer->frame->size->height = size->height;
}
void scrollViewSetContentOffset(CCScrollView* sv, CCPoint* offset) {
if (!sv) return;
float contentH = sv->contentSize->height;
float viewportH = sv->view->frame->size->height;
// Calculate Max Offset
float maxOffsetY = contentH - viewportH;
if (maxOffsetY < 0) maxOffsetY = 0; // Content is smaller than view
// --- DEBUG LOG ---
//printf("DEBUG SCROLL: Request=%.2f, ContentH=%.2f, ViewH=%.2f, MaxOffset=%.2f\n", offset->y, contentH, viewportH, maxOffsetY);
// -----------------
// Uses the ESP log system (safer)
FreeOSLogI("", "Scroll: Req=%.2f, Content=%.2f, View=%.2f", offset->y, contentH, viewportH);
// Clamp
if (offset->y < 0) offset->y = 0;
if (offset->y > maxOffsetY) offset->y = maxOffsetY;
// Update State
sv->contentOffset->y = offset->y;
sv->contentOffset->x = offset->x;
// Move View
sv->contentView->frame->origin->y = -(offset->y);
sv->contentView->layer->frame->origin->y = -(offset->y);
}
CCTextView* textViewWithFrame(CCRect* frame) {
CCTextView* tv = (CCTextView*)cc_safe_alloc(1, sizeof(CCTextView));
if (!tv) return NULL;
tv->type = CCType_TextView;
// 1. Create the Scroll Container
tv->scrollView = scrollViewWithFrame(frame);
// 2. Create the Label
// Width matches the frame, Height will be dynamic
tv->label = labelWithFrame(ccRect(0, 0, frame->size->width, frame->size->height));
tv->label->lineBreakMode = CCLineBreakWordWrapping;
tv->label->textAlignment = CCTextAlignmentLeft;
// Disable the border on the label itself (so you don't see a black box)
if (tv->label->view->layer) tv->label->view->layer->borderWidth = 0;
// 3. Add Label to Content
// ------------------------------------------------------------
// CRITICAL FIX: Add 'tv->label', NOT 'tv->label->view'
// This ensures the renderer sees it as CCType_Label (Text),
// not CCType_View (Rectangle).
// ------------------------------------------------------------
viewAddSubview(tv->scrollView->contentView, tv->label);
return tv;
}
/*void textViewSetText(CCTextView* tv, CCString* text, FT_Face face) {
if (!tv || !text) return;
// 1. Set the text
labelSetText(tv->label, text);
// 2. Calculate the required height
// We use your existing helper (make sure it's exposed in header)
// If you don't have the helper exposed, you can estimate: lines * fontSize
// Setup a temporary format for measurement
TextFormat fmt = {
.alignment = TEXT_ALIGN_LEFT,
.wrapMode = TEXT_WRAP_MODE_WHOLE_WORD,
.lineSpacing = (int)tv->label->lineSpacing,
.glyphSpacing = (int)tv->label->glyphSpacing
};
int calculatedHeight = measureTextHeight(
face,
text->string,
(int)tv->label->view->frame->size->width,
(int)tv->label->fontSize,
&fmt
);
// Add padding
calculatedHeight += 20;
// 2. Get the Current Viewport Width/Height
// We need to trust the width set in textViewWithFrame
float viewportWidth = tv->scrollView->view->frame->size->width;
// 3. Update the Label's Frame
tv->label->view->frame->size->height = (float)calculatedHeight;
tv->label->view->layer->frame->size->height = (float)calculatedHeight;
// 4. CRITICAL: Update the ScrollView Content Size
// This is what enables the scrolling math (MaxOffset = 365 - 300 = 65)
CCSize* newContentSize = ccSize(viewportWidth, (float)calculatedHeight);
scrollViewSetContentSize(tv->scrollView, newContentSize);
printf("calculated height of scroll view: %d", calculatedHeight);
// Clean up temp size struct if ccSize mallocs
free(newContentSize);
}*/
void textViewSetText(CCTextView* tv, CCString* text) {
if (!tv || !text) return;
// Safety Check: Ensure the cache properties are set!
if (!tv->ftManager || !tv->ftImageCache || !tv->ftCMapCache) {
printf("Error: TextView cache properties are NULL. Assign them before setting text.\n");
return;
}
// 1. Set the text content
labelSetText(tv->label, text);
// 2. Identify the Font ID (Path)
const char* fontPath = cStringOfString(tv->label->fontName);
//if (!fontPath) fontPath = "spiffs/fonts/Monitorica-Bd.ttf";
if (!fontPath) {
printf("\ntextViewSetText !fontPath\n");
}
printf("\nfontPath %s\n", fontPath);
// 3. Setup Format
TextFormat fmt = {
.alignment = TEXT_ALIGN_LEFT,
.wrapMode = TEXT_WRAP_MODE_WHOLE_WORD,
.lineSpacing = (int)tv->label->lineSpacing,
.glyphSpacing = (int)tv->label->glyphSpacing
};
// 4. Calculate Height using the STRUCT's properties
int calculatedHeight = measureTextHeightCached(
tv->ftManager, // <--- Use the struct property
tv->ftImageCache, // <--- Use the struct property
tv->ftCMapCache, // <--- Use the struct property
(FTC_FaceID)fontPath,
text->string,
(int)tv->label->view->frame->size->width,
(int)tv->label->fontSize,
&fmt
);
calculatedHeight += 20; // Padding
// 5. Update UI
float viewportWidth = tv->scrollView->view->frame->size->width;
printf("\ncalculatedHeight %d\n", calculatedHeight);
printf("\tv->label->view->layer %d\n", (tv->label->view->layer == NULL)? 0 : 1);
printf("\tv->label->view->layer->frame %d\n", (tv->label->view->layer->frame == NULL)? 0 : 1);
// v->label->view->layer 1
// v->label->view->layer->frame 0
tv->label->view->frame->size->height = (float)calculatedHeight;
tv->label->view->layer->frame->size->height = (float)calculatedHeight;
CCSize* newContentSize = ccSize(viewportWidth, (float)calculatedHeight);
printf("tv->scrollView %d %f %f\n", (tv->label->view->layer == NULL)? 0 : 1, newContentSize->width, newContentSize->height);
scrollViewSetContentSize(tv->scrollView, newContentSize);
free(newContentSize);
}
CCGraphicsContext* graphicsContextCreate(Float width, Float height) {
CCGraphicsContext* newGraphicsContext = (CCGraphicsContext*)cc_safe_alloc(1, sizeof(CCGraphicsContext));
newGraphicsContext->type = CCType_GraphicsContext;
if (!newGraphicsContext) return NULL;
newGraphicsContext->size = ccSize(width, height);
//GLuint fbo, texture;
//createFBO(&fbo, &texture, (int)width, (int)height);
//newGraphicsContext->fbo = fbo;
//newGraphicsContext->texture = texture;
return newGraphicsContext;
}
#define EPSILON 1e-6
CCTransform* transform(void) {
CCTransform* newTransform = (CCTransform*)cc_safe_alloc(1, sizeof(CCTransform));
if (!newTransform) return NULL;
newTransform->type = CCType_Transform;
return newTransform;
}
CCTransform* transformWithMatrix(float* matrix) {
CCTransform* newTransform = transform();
//newTransform->matrix = matrix;
return newTransform;
}
CCTransform* transformRotate(Float rotationAngle) {
CCTransform* newTransform = transform();
newTransform->rotationAngle = rotationAngle;
//newTransform->matrix = CCTransformRotateMatrix(rotationAngle);
return newTransform;;
}
CCTransform* transformTranslation(Float x, Float y) {
CCTransform* newTransform = transform();
newTransform->translation = ccPoint(x, y);
//newTransform->matrix = CCTransformTranslateMatrix(x, y);
return newTransform;;
}
CCTransform* transformScale(Float x, Float y) {
CCTransform* newTransform = transform();
newTransform->scale = ccSize(x, y);
//newTransform->matrix = CCTransformScaleMatrix(x, y);
return newTransform;;
}
CCTransform* transformConcat(CCTransform* transform1, CCTransform* transform2) {
CCTransform *newTransform = transform();
//newTransform->matrix = CCTransformConcatMatrix(transform1->matrix, transform2->matrix);
return newTransform;
}
bool transformEqualsTransform(CCTransform* transform1, CCTransform* transform2) {
//return transformEqualsTransformMatrix(transform1->matrix, transform2->matrix);
return false;
}
CCTransform3D* transform3D(void) {
CCTransform3D* newTransform = (CCTransform3D*)cc_safe_alloc(1, sizeof(CCTransform3D));
newTransform->type = CCType_Transform3D;
if (!newTransform) return NULL;
//newTransform->matrix = CCTransform3DIdentity();
return newTransform;
}
CCTransform3D* transform3DWithMatrix(CCTransform3DMatrix matrix){
CCTransform3D* newTransform = (CCTransform3D*)cc_safe_alloc(1, sizeof(CCTransform3D));
newTransform->type = CCType_Transform3D;
if (!newTransform) return NULL;
//newTransform->matrix = matrix;
return newTransform;
}
float afb(CCView* view) {
return view->frame->origin->y+view->frame->size->height;
}
float afr(CCView* view) {
return view->frame->origin->x+view->frame->size->width;
}
static Float windowWidth = 1280;
static Float windowHeight = 800;
static int drawLayerCount = 0;
void drawLayer(void* view) {
printf("draw layer: %d %s", drawLayerCount, cStringOfString(stringForType(((CCView*)view)->type)));
drawLayerCount++;
CCView* viewObject = (CCView*)view;
CCLayer* layer = NULL;
bool isImage = false;
bool isText = false;
CCImage* image = NULL;
if (viewObject->type == CCType_View) {
layer = viewObject->layer;
}
else if (viewObject->type == CCType_Label) {
layer = ((CCLabel*)viewObject)->view->layer;
isText = true;
}
else if (viewObject->type == CCType_ImageView) {
CCImageView* imageViewObject = ((CCImageView*)viewObject);
layer = imageViewObject->view->layer;
isImage = true;
image = imageViewObject->image;
}
if (!layer) {
printf("no layer error");
return;
}
printf("\ndraw layer of rect %f %f %f %f and color %f %f %f %f\n", layer->frame->origin->x, layer->frame->origin->y, layer->frame->size->width, layer->frame->size->height, layer->backgroundColor->r, layer->backgroundColor->g, layer->backgroundColor->b, layer->backgroundColor->a);
float cornerRadius = layer->cornerRadius; // Assume this field is added to the layer struct
if (cornerRadius == 0) {
}
else {
}
//Draw sublayers of layer
for (int i = 0; i < layer->sublayers->count; i++) {
CCLayer* layer1 = arrayObjectAtIndex(layer->sublayers, i);
drawLayer(layer1);
}
}
void drawView(void* view) {
//Draw this view's layer. Then draw all subviews of this view
printf("\ndraw drawView: %d %s\n", drawLayerCount, cStringOfString(stringForType(((CCView*)view)->type)));
drawLayer(view);
CCView* viewObject = NULL;
if (((CCView*)view)->type == CCType_View) {
viewObject = view;
}
else if (((CCView*)view)->type == CCType_Label) {
viewObject = ((CCLabel*)view)->view;
}
else if (((CCView*)view)->type == CCType_ImageView) {
viewObject = ((CCImageView*)view)->view;
}
if (!viewObject) {
printf("no view object error");
return;
}
for (int i = 0; i < viewObject->subviews->count; i++) {
CCView* view1 = arrayObjectAtIndex(viewObject->subviews, i);
drawView(view1);
}
}
static bool isMousePressed = false;
static CCView* mainWindowView;
static CCPoint* mousePosition;
void handleGestureOnView(CCView* view1) {
for (int ii = 0; ii < view1->gestureRecognizers->count; ii++) {
printf("\ngesture recognizer found\n");
CCGestureRecognizer* gestureRecognizer = arrayObjectAtIndex(view1->gestureRecognizers, ii);
if (rectContainsPoint(view1->frame, mousePosition)) {
CCGestureType gestureTypeForMouseEvent = (isMousePressed) ? CCGestureDown : CCGestureUp;
printf("\nrect contains point\n");
gestureRecognizer->gestureType = gestureTypeForMouseEvent;
if (gestureRecognizer->gestureType == gestureTypeForMouseEvent) {
gestureRecognizer->action(gestureRecognizer);
printf("\ngesture recognizer action\n");
}
}
}
}
void handleClickOnView(CCView* view) {
printf("\nhandle click on view\n");
CCView* view1 = view;
if (view1->type == CCType_Label) {
view1 = ((CCLabel*)view)->view;
}
else if (view1->type == CCType_ImageView) {
view1 = ((CCImageView*)view)->view;
}
handleGestureOnView(view1);
for (int i = 0; i < view1->subviews->count; i++) {
CCView* view2 = arrayObjectAtIndex(view1->subviews, i);
CCView* view3 = view2;
if (view2->type == CCType_Label) {
view3 = ((CCLabel*)view2)->view;
}
else if (view2->type == CCType_ImageView) {
view3 = ((CCImageView*)view2)->view;
}
handleGestureOnView(view3);
handleClickOnView(view2);
}
}
void handleClickOnWindowViews(void) {
handleClickOnView(mainWindowView);
}
static CCView* topBarView;
static bool isRed = false;
void topBarClickAction(void* gestureRecognizer) {
printf("\ntop click action\n");
if (!isRed) {
isRed = true;
viewSetBackgroundColor(topBarView, color(1, 0.2, 0.2, 1.0));
}
else {
isRed = false;
viewSetBackgroundColor(topBarView, color(0.2, 1.0, 1.0, 1.0));
}
shouldUpdate = true;
}
static CCImageView* imageView1;
void imageViewClickAction(void* gestureRecognizer) {
printf("imageViewClickAction");
imageView1->view->transform = transformRotate(M_PI_4);
shouldUpdate = true;
}
static CCString* programLocaleIdentifier = NULL;
static CCString* programTimeZoneIdentifier = NULL;
static CCLocale* programLocale = NULL;
static CCTimeZone* programTimeZone = NULL;
void loadMainProgramViews(void) {
mainWindowView = viewWithFrame(ccRect(0, 0, windowWidth, windowHeight));
mainWindowView->backgroundColor = color(1, 1, 1, 1);
topBarView = viewWithFrame(ccRect(0, 0, windowWidth-100, 120));
viewSetBackgroundColor(topBarView, color(0, 0, 0, 0.1));
layerSetCornerRadius(topBarView->layer, 30.0f);
viewAddSubview(mainWindowView, topBarView);
CCGestureRecognizer* topBarClick = gestureRecognizerWithType(CCGestureUp, topBarClickAction);
viewAddGestureRecognizer(topBarView, topBarClick);
CCView* bottomBarView = viewWithFrame(ccRect(0, windowHeight-120, windowWidth-200, 120));
viewSetBackgroundColor(bottomBarView, color(0, 1, 0, 1.0));
layerSetCornerRadius(bottomBarView->layer, 30.0f);
viewAddSubview(mainWindowView, bottomBarView);
imageView1 = imageViewWithFrame(ccRect(500, 200, 100, 100));
CCImage* imageObject = imageWithFile(ccs("testImage.png"));
//CCImage* imageObject = imageWithFile(resourceFilePath(ccs("testImage.png")));
//size_t dataSize;
//unsigned char* data = rgbaToPngData(imageObject->imageData, imageObject->size->width, imageObject->size->height, &dataSize);
//CCData* data1 = dataWithBytes(data, dataSize);
//dataWriteToFile(data1, ccs("/Users/chrisgalzerano/Desktop/testImage767.png"));
imageViewSetImage(imageView1, imageObject);
viewAddSubview(mainWindowView, imageView1);
CCGestureRecognizer* imageViewClick = gestureRecognizerWithType(CCGestureUp, imageViewClickAction);
viewAddGestureRecognizer(imageView1, imageViewClick);
printf("\nmainWindowView subviews: %d\n", mainWindowView->subviews->count);
}
void printCurrentWorkingDirectory() {
char cwd[PATH_MAX]; // Define a buffer to hold the current working directory path
// Attempt to get the current working directory
if (getcwd(cwd, sizeof(cwd)) != NULL) {
printf("Current Working Directory: %s\n", cwd);
} else {
perror("getcwd() error"); // Print an error message if getcwd fails
}
}
char* getResourceFilePath(const char* pathToAppend) {
char cwd[PATH_MAX]; // Buffer to store the current working directory
// Attempt to get the current working directory
if (getcwd(cwd, sizeof(cwd)) == NULL) {
perror("getcwd() error");
return NULL;
}
// Calculate the total length needed for the resulting path
size_t totalLength = strlen(cwd) + strlen(pathToAppend) + 2; // Extra space for '/' and '\0'
// Allocate memory for the full path
char* fullPath = (char*)cc_safe_alloc(1, totalLength);
if (fullPath == NULL) {
perror("cc_safe_alloc(1, ) error");
return NULL;
}
// Construct the full path
snprintf(fullPath, totalLength, "%s/%s", cwd, pathToAppend);
return fullPath;
}
CCString* resourceFilePath(CCString* filePath) {
return ccs(getResourceFilePath(cStringOfString(filePath)));
}
static CCShapeLayer* shapeLayerTest;
int mainProgram(void) {
programLocaleIdentifier = ccs("en_US");
programTimeZoneIdentifier = ccs("America\/New_York");
CCArray* points = arrayWithObjects(ccPoint(100, 100), ccPoint(100, 200), ccPoint(200, 200), ccPoint(200, 30), NULL);
/*CCArray *points = array();
arrayAddObject(points, ccPoint(100, 100));
arrayAddObject(points, ccPoint(100, 200));
arrayAddObject(points, ccPoint(200, 100));
arrayAddObject(points, ccPoint(200, 30));*/
CCString* testPath = ccs("/Users/chrisgalzerano/Desktop");
CCArray* components = stringComponentsSeparatedByString(testPath, ccs("/"));
printf("\ncomponents %d\n", components->count);
for (int i = 0; i < components->count; i++) {
printf("\ncomponent %d %s\n", i, cStringOfString(arrayObjectAtIndex(components, i)));
}
printf("main program points %d", points->count);
shapeLayerTest = shapeLayerWithPointPath(pointPathWithPoints(points));
shapeLayerTest->fillColor = color(0, 1.0, 0, 1.0);
printCurrentWorkingDirectory();
// Dirty state to track if the graphics need to be re-rendered
bool isDirty = true;
loadMainProgramViews();
//CCLocale* locale = localeWithIdentifier(programLocaleIdentifier);
//CCTimeZone* timeZone = timeZoneWithName(programTimeZoneIdentifier);
const char* fontPath = getResourceFilePath("proximanovaRegular.ttf");
int fontSize = 48;
//LoadFont(fontPath, fontSize);
//defaultFont = font(ccs(fontPath), 48);
CCView* viewTest = viewWithFrame(ccRect(100, 100, 200, 120));
viewSetBackgroundColor(viewTest, color(0, 1, 0, 1));
CCLayer* layer = viewTest->layer;
return 0;
}
//String Functions
// Function to create a CCString from a C string
CCString* string(void) {
CCString* newString = (CCString*)cc_safe_alloc(1, sizeof(CCString));
newString->type = CCType_String;
if (!newString) {
return NULL;
}
newString->length = 0;
newString->string = "";
return newString;
}
CCString* ccs(const char * string) {
if (string == NULL) {
return NULL;
}
CCString* newString = (CCString*)cc_safe_alloc(1, sizeof(CCString));
newString->type = CCType_String;
if (!newString) {
return NULL;
}
newString->length = strlen(string);
newString->string = (char*)cc_safe_alloc(1, newString->length + 1); // +1 for null terminator
if (newString->string) {
strcpy(newString->string, string);
} else {
free(newString);
return NULL;
}
return newString;
}
// Function to create a CCString from a C string (identical to ccs)
CCString* stringWithCString(const char * string) {
return ccs(string);
}
// Function to create a CCString with formatted content
CCString* stringWithFormat(const char* format, ...) {
if (format == NULL) {
return NULL;
}
va_list args;
va_start(args, format);
int size = vsnprintf(NULL, 0, format, args) + 1; // Determine the size needed
va_end(args);
va_start(args, format);
char* buffer = (char*)cc_safe_alloc(1, size);
if (!buffer) {
va_end(args);
return NULL;
}
vsnprintf(buffer, size, format, args);
va_end(args);
CCString* formattedString = (CCString*)cc_safe_alloc(1, sizeof(CCString));
formattedString->type = CCType_String;
if (!formattedString) {
free(buffer);
return NULL;
}
formattedString->length = size - 1; // Exclude null terminator
formattedString->string = buffer;
return formattedString;
}
// Function to create a substring based on a specified range
CCString* substringWithRange(CCString* string, CCRange range) {
if (string == NULL || string->string == NULL || range.loc + range.len > string->length) {
return NULL;
}
CCString* substring = (CCString*)cc_safe_alloc(1, sizeof(CCString));
substring->type = CCType_String;
if (!substring) {
return NULL;
}
substring->length = range.len;
substring->string = (char*)cc_safe_alloc(1, substring->length + 1);
if (substring->string) {
strncpy(substring->string, string->string + range.loc, range.len);
substring->string[range.len] = '\0';
} else {
free(substring);
return NULL;
}
return substring;
}
// Function to create a substring from a specific index to the end of the string
CCString* substringFromIndex(CCString* string, int index) {
if (string == NULL || string->string == NULL || index < 0 || index > string->length) {
return NULL;
}
CCString* substring = (CCString*)cc_safe_alloc(1, sizeof(CCString));
substring->type = CCType_String;
if (!substring) {
return NULL;
}
substring->length = string->length - index;
substring->string = (char*)cc_safe_alloc(1, substring->length + 1);
if (substring->string) {
strcpy(substring->string, string->string + index);
} else {
free(substring);
return NULL;
}
return substring;
}
// Function to create a substring from the beginning of the string up to a specific index
CCString* substringToIndex(CCString* string, int index) {
if (string == NULL || string->string == NULL || index < 0 || index > string->length) {
return NULL;
}
CCString* substring = (CCString*)cc_safe_alloc(1, sizeof(CCString));
substring->type = CCType_String;
if (!substring) {
return NULL;
}
substring->length = index;
substring->string = (char*)cc_safe_alloc(1, substring->length + 1);
if (substring->string) {
strncpy(substring->string, string->string, index);
substring->string[index] = '\0';
} else {
free(substring);
return NULL;
}
return substring;
}
// Function to append one CCString to another and return a new CCString
CCString* stringByAppendingString(CCString* string, CCString* string1) {
if (!string || !string->string) return string1;
if (!string1 || !string1->string) return string;
// Allocate space for the new concatenated string
int newLength = string->length + string1->length;
char* newStr = (char*)cc_safe_alloc(1, newLength + 1); // +1 for null terminator
if (!newStr) return NULL;
// Copy both strings into the new allocated space
strcpy(newStr, string->string);
strcat(newStr, string1->string);
// Create new CCString
CCString* newString = (CCString*)cc_safe_alloc(1, sizeof(CCString));
newString->type = CCType_String;
if (!newString) {
free(newStr);
return NULL;
}
newString->length = newLength;
newString->string = newStr;
return newString;
}
// Function to append formatted content to a CCString and return a new CCString
CCString* stringByAppendingFormat(CCString* string, const char* format, ...) {
if (!string || !string->string || !format) return NULL;
va_list args;
va_start(args, format);
// Calculate length needed for formatted string
int formatLength = vsnprintf(NULL, 0, format, args);
va_end(args);
if (formatLength < 0) return NULL;
va_start(args, format);
char* formatStr = (char*)cc_safe_alloc(1, formatLength + 1);
if (!formatStr) {
va_end(args);
return NULL;
}
vsnprintf(formatStr, formatLength + 1, format, args);
va_end(args);
// Create the concatenated string
int newLength = string->length + formatLength;
char* newStr = (char*)cc_safe_alloc(1, newLength + 1);
if (!newStr) {
free(formatStr);
return NULL;
}
strcpy(newStr, string->string);
strcat(newStr, formatStr);
free(formatStr);
// Create new CCString
CCString* newString = (CCString*)cc_safe_alloc(1, sizeof(CCString));
newString->type = CCType_String;
if (!newString) {
free(newStr);
return NULL;
}
newString->length = newLength;
newString->string = newStr;
return newString;
}
CCArray* stringPathComponents(CCString* string) {
return stringComponentsSeparatedByString(string, ccs("/"));
}
CCString* stringLastPathComponent(CCString* string) {
CCArray* array = stringComponentsSeparatedByString(string, ccs("/"));
if (array->count > 0) {
return arrayObjectAtIndex(array, array->count-1);
}
return NULL;
}
CCString* stringFileExtension(CCString* string) {
CCString* lastPathComponent = stringLastPathComponent(string);
CCArray* components = stringComponentsSeparatedByString(lastPathComponent, ccs("."));
if (components->count > 1) {
return arrayObjectAtIndex(components, 1);
}
else {
return ccs("");
}
}
bool stringContainsString(CCString* string, CCString* containsString) {
if (string == NULL || string->string == NULL || containsString == NULL || containsString->string == NULL) {
return false;
}
// Use strstr to check if containsString->string is found within string->string
return strstr(string->string, containsString->string) != NULL;
}
// Function to check if one CCString is equal to another CCString
bool stringEqualsString(CCString* string, CCString* equalsString) {
if (string == NULL || string->string == NULL || equalsString == NULL || equalsString->string == NULL) {
return false;
}
// Use strcmp to check for string equality
return strcmp(string->string, equalsString->string) == 0;
}
CCString* stringLowercase(CCString* string) {
if (string == NULL || string->string == NULL) {
return NULL;
}
CCString* lowerString = ccs(string->string);
for (UInteger i = 0; i < lowerString->length; i++) {
lowerString->string[i] = tolower(lowerString->string[i]);
}
return lowerString;
}
// Function to convert the entire CCString to uppercase
CCString* stringUppercase(CCString* string) {
if (string == NULL || string->string == NULL) {
return NULL;
}
CCString* upperString = ccs(string->string);
for (UInteger i = 0; i < upperString->length; i++) {
upperString->string[i] = toupper(upperString->string[i]);
}
return upperString;
}
// Function to capitalize the first letter of each word in a CCString
CCString* stringCapitalized(CCString* string) {
if (string == NULL || string->string == NULL) {
return NULL;
}
CCString* capitalizedString = ccs(string->string);
bool newWord = true;
for (UInteger i = 0; i < capitalizedString->length; i++) {
if (isspace(capitalizedString->string[i])) {
newWord = true;
} else if (newWord) {
capitalizedString->string[i] = toupper(capitalizedString->string[i]);
newWord = false;
} else {
capitalizedString->string[i] = tolower(capitalizedString->string[i]);
}
}
return capitalizedString;
}
// Function to append one CCString to another, modifying the first CCString
void appendString(CCString* string, CCString* stringToAppend) {
if (!string || !string->string || !stringToAppend || !stringToAppend->string) return;
int newLength = string->length + stringToAppend->length;
char* newStr = (char*)cc_safe_realloc(string->string, newLength + 1); // +1 for null terminator
if (!newStr) return;
strcat(newStr, stringToAppend->string);
string->string = newStr;
string->length = newLength;
}
// Function to append formatted content, modifying the original CCString
void appendFormat(CCString* string, const char* format, ...) {
if (!string || !string->string || !format) return;
va_list args;
va_start(args, format);
// Calculate length needed for formatted string
int formatLength = vsnprintf(NULL, 0, format, args);
va_end(args);
if (formatLength < 0) return;
va_start(args, format);
char* formatStr = (char*)cc_safe_alloc(1, formatLength + 1);
if (!formatStr) {
va_end(args);
return;
}
vsnprintf(formatStr, formatLength + 1, format, args);
va_end(args);
int newLength = string->length + formatLength;
char* newStr = (char*)cc_safe_realloc(string->string, newLength + 1);
if (!newStr) {
free(formatStr);
return;
}
strcat(newStr, formatStr);
free(formatStr);
string->string = newStr;
string->length = newLength;
}
CCString* replaceOccurencesOfStringWithString(CCString* string, CCString* target, CCString* replacement) {
if (string == NULL || target == NULL || replacement == NULL ||
string->string == NULL || target->string == NULL || replacement->string == NULL) {
return NULL;
}
// Calculate occurrences of target in string.
int occurrences = 0;
const char* tempStr = string->string;
while ((tempStr = strstr(tempStr, target->string)) != NULL) {
occurrences++;
tempStr += target->length;
}
// Calculate new length after replacement.
int newLength = string->length + occurrences * (replacement->length - target->length);
char* buffer = (char*)cc_safe_alloc(1, newLength + 1); // +1 for null terminator
if (!buffer) {
return NULL;
}
// Create the new string with replacements.
char* srcPtr = string->string;
char* dstPtr = buffer;
const char* matchPtr;
while ((matchPtr = strstr(srcPtr, target->string)) != NULL) {
int prefixLength = matchPtr - srcPtr;
strncpy(dstPtr, srcPtr, prefixLength);
dstPtr += prefixLength;
strncpy(dstPtr, replacement->string, replacement->length);
dstPtr += replacement->length;
srcPtr = matchPtr + target->length;
}
// Copy remaining part of the original string.
strcpy(dstPtr, srcPtr);
CCString* resultString = (CCString*)cc_safe_alloc(1, sizeof(CCString));
resultString->type = CCType_Array;
if (!resultString) {
free(buffer);
return NULL;
}
resultString->length = newLength;
resultString->string = buffer;
return resultString;
}
// Function to split a string into components using a separator string
CCArray* stringComponentsSeparatedByString(CCString* string, CCString* separator) {
if (string == NULL || separator == NULL || string->string == NULL || separator->string == NULL) {
return NULL;
}
int occurrences = 0;
const char* tempStr = string->string;
while ((tempStr = strstr(tempStr, separator->string)) != NULL) {
occurrences++;
tempStr += separator->length;
}
CCArray* components = (CCArray*)cc_safe_alloc(1, sizeof(CCArray));
components->type = CCType_Array;
if (!components) return NULL;
components->count = occurrences + 1;
components->array = cc_safe_alloc(1, (occurrences + 1) * sizeof(void*));
if (!components->array) {
free(components);
return NULL;
}
const char* start = string->string;
const char* end = NULL;
int index = 0;
while ((end = strstr(start, separator->string)) != NULL) {
int partLength = end - start;
CCString* part = (CCString*)cc_safe_alloc(1, sizeof(CCString));
part->type = CCType_String;
if (!part) {
free(components->array);
free(components);
return NULL;
}
part->length = partLength;
part->string = (char*)cc_safe_alloc(1, partLength + 1);
strncpy(part->string, start, partLength);
part->string[partLength] = '\0';
components->array[index++] = part;
start = end + separator->length;
}
CCString* part = (CCString*)cc_safe_alloc(1, sizeof(CCString));
part->type = CCType_String;
if (!part) {
free(components->array);
free(components);
return NULL;
}
int partLength = string->length - (start - string->string);
part->length = partLength;
part->string = (char*)cc_safe_alloc(1, partLength + 1);
strcpy(part->string, start);
components->array[index] = part;
//This might need mixed into the function above but is
//technically fine like this for now or a while
//bug where this array has first item blank as one extra object
if (((CCString*)arrayObjectAtIndex(components, 0))->length == 0) {
arrayDeleteObjectAtIndex(components, 0);
}
return components;
}
// Function to combine an array of strings into a single string with a combiner
CCString* stringsCombinedWithString(CCArray* strings, CCString* combiner) {
if (strings == NULL || combiner == NULL || strings->count == 0) {
return NULL;
}
int newLength = 0;
// Calculate combined string length including combiners
for (int i = 0; i < strings->count; ++i) {
CCString* stringPart = (CCString*)strings->array[i];
newLength += stringPart->length;
}
newLength += (strings->count - 1) * combiner->length;
// Allocate memory for combined string
char* combinedString = (char*)cc_safe_alloc(1, newLength + 1); // +1 for null terminator
if (!combinedString) {
return NULL;
}
// Build the combined string
char* current = combinedString;
for (int i = 0; i < strings->count; ++i) {
CCString* stringPart = (CCString*)strings->array[i];
strcpy(current, stringPart->string);
current += stringPart->length;
if (i < strings->count - 1) {
strcpy(current, combiner->string);
current += combiner->length;
}
}
*current = '\0'; // Null-terminate
CCString* result = (CCString*)cc_safe_alloc(1, sizeof(CCString));
result->type = CCType_String;
if (!result) {
free(combinedString);
return NULL;
}
result->length = newLength;
result->string = combinedString;
return result;
}
CCData* stringDataWithEncoding(CCString* string, StringEncoding encoding) {
if (!string || !string->string || encoding != UTF8StringEncoding) {
return NULL; // Handle invalid input or unsupported encoding
}
CCData* data = (CCData*)cc_safe_alloc(1, sizeof(CCData));
data->type = CCType_Data;
if (!data) {
return NULL;
}
data->length = string->length;
data->bytes = cc_safe_alloc(1, data->length);
if (!data->bytes) {
free(data);
return NULL;
}
memcpy(data->bytes, string->string, data->length);
return data;
}
CCString* stringFromDataWithEncoding(CCData* data, StringEncoding encoding) {
if (!data || !data->bytes || encoding != UTF8StringEncoding) {
return NULL; // Handle invalid input or unsupported encoding
}
CCString* string = (CCString*)cc_safe_alloc(1, sizeof(CCString));
string->type = CCType_String;
if (!string) {
return NULL;
}
string->length = (int)data->length;
string->string = (char*)cc_safe_alloc(1, data->length + 1);
if (!string->string) {
free(string);
return NULL;
}
memcpy(string->string, data->bytes, data->length);
string->string[data->length] = '\0'; // Null-terminate the string
return string;
}
CCString* stringWithContentsOfFile(CCString* filePath) {
CCData* fileData = dataWithContentsOfFile(filePath);
CCString* fileString = stringFromDataWithEncoding(fileData, UTF8StringEncoding);
return fileString;
}
// Function to get the C-style string from a CCString
const char* cStringOfString(CCString* string) {
if (!string) {
return NULL; // Handle the case where the input is NULL
}
return string->string; // Return the internal C-style string
}
// Function to convert a CCString to an int
int stringIntValue(CCString* string) {
if (!string || !string->string) {
return 0; // Default return value
}
return (int)strtol(string->string, NULL, 10); // Convert to long and cast to int
}
// Function to convert a CCString to a float
float stringFloatValue(CCString* string) {
if (!string || !string->string) {
return 0.0f; // Default return value
}
return strtof(string->string, NULL); // Direct conversion to float
}
// Function to convert a CCString to a long
long stringLongValue(CCString* string) {
if (!string || !string->string) {
return 0L; // Default return value
}
return strtol(string->string, NULL, 10); // Use base 10
}
// Function to convert a CCString to a double
double stringDoubleValue(CCString* string) {
if (!string || !string->string) {
return 0.0; // Default return value
}
return strtod(string->string, NULL); // Direct conversion to double
}
// Function to copy a CCString
// Function to copy a CCString
CCString* copyCCString(const CCString* original) {
if (!original) return NULL; // Handle NULL input for safety
// Allocate memory for the new CCString
CCString* copy = (CCString*)cc_safe_alloc(1, sizeof(CCString));
if (!copy) return NULL; // Check allocation
// Copy the type and length
copy->type = original->type;
copy->length = original->length;
// We MUST allocate heap memory even for empty strings to prevent tlsf_free ROM crashes!
if (original->string) {
// Allocate memory for the string copy
copy->string = (char*)cc_safe_alloc(1, original->length + 1); // +1 for null terminator
if (!copy->string) {
free(copy); // Free the CCString structure if string allocation fails
return NULL;
}
// Copy the string contents (Safe for both populated and empty strings)
strncpy(copy->string, original->string, original->length);
copy->string[original->length] = '\0'; // Ensure null termination
} else {
copy->string = NULL; // Safe to pass NULL to free() later
}
return copy;
}
void freeCCString(CCString* ccstring) {
if (ccstring) {
// Only free if `string` is dynamically allocated
// This assumes that only non-empty strings were dynamically allocated
if (ccstring->string && ccstring->string[0] != '\0') {
free(ccstring->string);
}
free(ccstring);
}
}
//Number Functions
// Function to create a CCNumber from an int
CCNumber* numberWithInt(int number) {
CCNumber* newNumber = (CCNumber*)cc_safe_alloc(1, sizeof(CCNumber));
newNumber->type = CCType_Number;
if (!newNumber) {
return NULL;
}
newNumber->doubleValue = (double)number;
return newNumber;
}
// Function to create a CCNumber from a float
CCNumber* numberWithFloat(float number) {
CCNumber* newNumber = (CCNumber*)cc_safe_alloc(1, sizeof(CCNumber));
newNumber->type = CCType_Number;
if (!newNumber) {
return NULL;
}
newNumber->doubleValue = (double)number;
return newNumber;
}
// Function to create a CCNumber from a long
CCNumber* numberWithLong(long number) {
CCNumber* newNumber = (CCNumber*)cc_safe_alloc(1, sizeof(CCNumber));
newNumber->type = CCType_Number;
if (!newNumber) {
return NULL;
}
newNumber->doubleValue = (double)number;
return newNumber;
}
// Function to create a CCNumber from a double
CCNumber* numberWithDouble(double number) {
CCNumber* newNumber = (CCNumber*)cc_safe_alloc(1, sizeof(CCNumber));
newNumber->type = CCType_Number;
if (!newNumber) {
return NULL;
}
newNumber->doubleValue = number;
return newNumber;
}
// Function to get the int value from a CCNumber
int numberIntValue(CCNumber* number) {
if (!number) return 0;
return (int)number->doubleValue;
}
// Function to get the float value from a CCNumber
float numberFloatValue(CCNumber* number) {
if (!number) return 0.0f;
return (float)number->doubleValue;
}
// Function to get the long value from a CCNumber
long numberLongValue(CCNumber* number) {
if (!number) return 0L;
return (long)number->doubleValue;
}
// Function to get the double value from a CCNumber
double numberDoubleValue(CCNumber* number) {
if (!number) return 0.0;
return number->doubleValue;
}
CCNumber* copyCCNumber(const CCNumber* original) {
if (!original) return NULL;
CCNumber* copy = (CCNumber*)cc_safe_alloc(1, sizeof(CCNumber));
if (!copy) return NULL;
copy->type = original->type;
copy->doubleValue = original->doubleValue;
return copy;
}
// Free function for CCNumber
void freeCCNumber(CCNumber* number) {
if (number) {
free(number);
}
}
//Array Functions
CCArray* array(void) {
// OLD BROKEN CODE:
// CCArray* arr = (CCArray*)cc_safe_alloc(1, sizeof(CCArray));
// arr->type = CCType_Array;
// return arr;
// NEW FIXED CODE:
CCArray* arr = (CCArray*)cc_safe_alloc(1, sizeof(CCArray)); // Zero out memory!
if (!arr) return NULL;
arr->type = CCType_Array;
arr->count = 0; // Explicitly start at 0
arr->array = NULL; // Explicitly start with no buffer
return arr;
}
// Function to create a new CCArray with the same contents as the given array
CCArray* arrayWithArray(CCArray* sourceArray) {
if (!sourceArray || sourceArray->count < 0) {
return NULL;
}
CCArray* newArray = (CCArray*)cc_safe_alloc(1, sizeof(CCArray));
newArray->type = CCType_Array;
if (!newArray) {
return NULL;
}
newArray->count = sourceArray->count;
if (newArray->count > 0) {
newArray->array = (void**)cc_safe_alloc(1, newArray->count * sizeof(void*));
if (!newArray->array) {
free(newArray);
return NULL;
}
// Copy elements from source array to new array
for (int i = 0; i < newArray->count; i++) {
newArray->array[i] = sourceArray->array[i];
}
} else {
newArray->array = NULL;
}
return newArray;
}
// Function to create a CCArray with a list of objects terminated by NULL
CCArray* arrayWithObjects(void* firstObject, ...) {
va_list args;
// Count the number of objects
int count = 0;
void* currentObject = firstObject;
va_start(args, firstObject);
while (currentObject != NULL) {
count++;
currentObject = va_arg(args, void*);
}
va_end(args);
// Allocate memory for CCArray
CCArray* newArray = (CCArray*)cc_safe_alloc(1, sizeof(CCArray));
if (!newArray) {
return NULL;
}
newArray->type = CCType_Array;
newArray->count = count;
newArray->array = (void**)cc_safe_alloc(1, count * sizeof(void*));
if (!newArray->array) {
free(newArray);
return NULL;
}
// Populate newArray
va_start(args, firstObject);
currentObject = firstObject; // Reset to the firstObject
for (int i = 0; i < count; i++) {
newArray->array[i] = currentObject;
currentObject = va_arg(args, void*);
}
va_end(args);
return newArray;
}
int arrayIndexOfObject(CCArray* ccArray, void* object) {
if (!ccArray || !object || ccArray->count == 0) return -1;
for (int i = 0; i < ccArray->count; i++) {
// Compare the memory addresses
if (ccArray->array[i] == object) {
return i;
}
}
return -1; // Not found
}
void arrayRemoveObject(CCArray* ccArray, void* object) {
if (!ccArray || !object || ccArray->count == 0) return;
int index = arrayIndexOfObject(ccArray, object);
if (index != -1) {
// Calculate how many items are AFTER the one we are removing
int numToMove = ccArray->count - 1 - index;
// Shift memory down to fill the gap
if (numToMove > 0) {
memmove(
&ccArray->array[index], // Destination (current slot)
&ccArray->array[index + 1], // Source (next slot)
numToMove * sizeof(void*)
);
}
// Nullify the last slot (good hygiene, though not strictly required since we decrement count)
ccArray->array[ccArray->count - 1] = NULL;
// Decrease count
ccArray->count--;
// OPTIONAL: Shrink memory (Realloc down)
// Since your AddObject reallocs every time, you might want to match that pattern,
// but it is usually safer/faster to just leave the capacity as is.
// If you want to be strict:
// ccArray->array = cc_safe_realloc(ccArray->array, ccArray->count * sizeof(void*));
}
}
// Function to add an object to the end of the array
void arrayAddObject(CCArray* array, void* object) {
if (!array) return;
// Allocate a new larger array to hold the existing objects plus the new one
void** newArray = cc_safe_realloc(array->array, (array->count + 1) * sizeof(void*));
if (!newArray) return; // Handle allocation failure
// Assign the new array back and add the new object
array->array = newArray;
array->array[array->count] = object;
array->count++;
}
// Function to add all objects from another array to the end of the array
void arrayAddObjectsFromArray(CCArray* array, CCArray* array2) {
if (!array || !array2) return;
// Allocate a new larger array to hold all objects
void** newArray = cc_safe_realloc(array->array, (array->count + array2->count) * sizeof(void*));
if (!newArray) return;
// Copy objects from array2 to the end of the new array
for (int i = 0; i < array2->count; i++) {
newArray[array->count + i] = array2->array[i];
}
array->array = newArray;
array->count += array2->count;
}
// Function to insert an object at a specific index in the array
void arrayInsertObjectAtIndex(CCArray* array, void* object, UInteger index) {
if (!array || index > array->count) return;
// Allocate a new larger array to insert the new object
void** newArray = cc_safe_realloc(array->array, (array->count + 1) * sizeof(void*));
if (!newArray) return;
// Shift elements to make space for the new object
for (UInteger i = array->count; i > index; i--) {
newArray[i] = newArray[i - 1];
}
// Insert new object
newArray[index] = object;
array->array = newArray;
array->count++;
}
// Function to get the object at a specific index
void* arrayObjectAtIndex(CCArray* array, UInteger index) {
if (!array || index >= array->count) return NULL;
return array->array[index];
}
// Function to delete the object at a specific index
void arrayDeleteObjectAtIndex(CCArray* array, UInteger index) {
if (!array || index >= array->count) return;
// Shift elements to fill the gap of removed object
for (UInteger i = index; i < array->count - 1; i++) {
array->array[i] = array->array[i + 1];
}
// Reallocate a smaller array
void** newArray = cc_safe_realloc(array->array, (array->count - 1) * sizeof(void*));
if (newArray || array->count == 1) { // Allow shrinkage but handle the case where count becomes 0
array->array = newArray;
}
array->count--;
}
// Function to get the count of objects in the array
UInteger arrayCount(CCArray* array) {
if (!array) return 0;
return array->count;
}
CCArray* copyCCArray(const CCArray* original) {
if (!original) return NULL;
CCArray* copy = (CCArray*)cc_safe_alloc(1, sizeof(CCArray));
if (!copy) return NULL;
copy->type = original->type;
copy->count = original->count;
copy->array = (void**)cc_safe_alloc(1, copy->count * sizeof(void*));
if (!copy->array) {
free(copy);
return NULL;
}
for (int i = 0; i < original->count; i++) {
copy->array[i] = copyElement(original->array[i]);
if (!copy->array[i]) {
// Handle error: free allocated resources
for (int j = 0; j < i; j++) {
freeElement(copy->array[j]);
}
free(copy->array);
free(copy);
return NULL;
}
}
return copy;
}
void freeCCArray(CCArray* array) {
if (!array) return;
// Free each element in the array
if (array->array) {
for (int i = 0; i < array->count; i++) {
if (array->array[i]) {
freeElement(array->array[i]);
}
}
free(array->array);
}
// Free the CCArray struct itself
free(array);
}
void* copyElement(void* element) {
if (!element) return NULL;
CCType type = *(CCType*)element;
switch (type) {
case CCType_String:
return copyCCString((CCString*)element);
case CCType_Number:
return copyCCNumber((CCNumber*)element);
case CCType_Data:
return copyCCData((CCData*)element);
case CCType_Date:
return copyCCDate((CCDate*)element);
case CCType_SortDescriptor:
return copyCCSortDescriptor((CCSortDescriptor*)element);
case CCType_Array:
return copyCCArray((CCArray*)element);
case CCType_Dictionary:
return copyCCDictionary((CCDictionary*)element);
default:
return NULL;
}
}
void freeElement(void* element) {
if (!element) return;
CCType type = *(CCType*)element;
switch (type) {
case CCType_String:
freeCCString((CCString*)element);
break;
case CCType_Number:
freeCCNumber((CCNumber*)element);
break;
case CCType_Data:
freeCCData((CCData*)element);
break;
case CCType_Date:
freeCCDate((CCDate*)element);
break;
case CCType_SortDescriptor:
freeCCSortDescriptor((CCSortDescriptor*)element);
break;
case CCType_Array:
freeCCArray((CCArray*)element);
break;
case CCType_Dictionary:
freeCCDictionary((CCDictionary*)element);
break;
default:
break;
}
}
//Dictionary Functions
// Function to create and return an empty CCDictionary
CCDictionary* dictionary(void) {
CCDictionary* newDict = (CCDictionary*)cc_safe_alloc(1, sizeof(CCDictionary));
newDict->type = CCType_Dictionary;
if (!newDict) {
return NULL;
}
newDict->count = 0;
newDict->items = NULL;
return newDict;
}
// Function to create a new CCDictionary with the same contents as the given dictionary
CCDictionary* dictionaryWithDictionary(CCDictionary* dictionary) {
if (!dictionary) {
return NULL;
}
CCDictionary* newDict = (CCDictionary*)cc_safe_alloc(1, sizeof(CCDictionary));
newDict->type = CCType_Dictionary;
if (!newDict) {
return NULL;
}
newDict->count = dictionary->count;
newDict->items = (CCKeyValuePair*)cc_safe_alloc(1, newDict->count * sizeof(CCKeyValuePair));
if (!newDict->items) {
free(newDict);
return NULL;
}
for (Integer i = 0; i < newDict->count; i++) {
newDict->items[i].key = dictionary->items[i].key;
newDict->items[i].value = dictionary->items[i].value;
}
return newDict;
}
// Function to create a CCDictionary from a list of key-value pairs
CCDictionary* dictionaryWithKeysAndObjects(CCString* key, ...) {
va_list args;
va_start(args, key);
CCDictionary* newDict = dictionary();
if (!newDict) {
va_end(args);
return NULL;
}
CCString* currentKey = key;
while (currentKey != NULL) {
void* value = va_arg(args, void*);
// Add the key-value pair to the dictionary
dictionarySetObjectForKey(newDict, value, currentKey);
// Move to the next key
currentKey = va_arg(args, CCString*);
}
va_end(args);
return newDict;
}
// Function to set an object for a specific key in the dictionary
void dictionarySetObjectForKey(CCDictionary* dictionary, void* object, CCString* key) {
if (!dictionary || !key || !key->string) return;
// Check if the key already exists and update the value
for (Integer i = 0; i < dictionary->count; i++) {
if (strcmp(dictionary->items[i].key->string, key->string) == 0) {
dictionary->items[i].value = object;
return;
}
}
// If key does not exist, add new key-value pair
CCKeyValuePair* newItems = (CCKeyValuePair*)cc_safe_realloc(dictionary->items, (dictionary->count + 1) * sizeof(CCKeyValuePair));
if (!newItems) return;
dictionary->items = newItems;
dictionary->items[dictionary->count].key = key;
dictionary->items[dictionary->count].value = object;
dictionary->count++;
}
// Function to get the object for a specific key in the dictionary
void* dictionaryObjectForKey(CCDictionary* dictionary, CCString* key) {
if (!dictionary || !key) return NULL;
for (Integer i = 0; i < dictionary->count; i++) {
if (strcmp(dictionary->items[i].key->string, key->string) == 0) {
return dictionary->items[i].value;
}
}
return NULL; // Return NULL if key is not found
}
// Function to get the object for a specific key, then free the key used for lookup
void* dictionaryObjectForKeyFreeKey(CCDictionary* dictionary, CCString* key) {
// If key is NULL, there's nothing to find and nothing to free
if (!key) return NULL;
void* result = NULL;
// Only search if the dictionary is valid
if (dictionary) {
for (Integer i = 0; i < dictionary->count; i++) {
// Safety check: ensure the dictionary item's key and string exist
if (dictionary->items[i].key && dictionary->items[i].key->string) {
if (strcmp(dictionary->items[i].key->string, key->string) == 0) {
result = dictionary->items[i].value;
break; // Found it, stop searching
}
}
}
}
// Safely free the temporary lookup key before we exit the function
freeCCString(key);
return result;
}
// Function to get all keys in the dictionary
CCArray* dictionaryAllKeys(CCDictionary* dictionary) {
if (!dictionary) return NULL;
CCArray* keysArray = (CCArray*)cc_safe_alloc(1, sizeof(CCArray));
keysArray->type = CCType_Array;
if (!keysArray) return NULL;
keysArray->count = dictionary->count;
keysArray->array = (void**)cc_safe_alloc(1, dictionary->count * sizeof(void*));
if (!keysArray->array) {
free(keysArray);
return NULL;
}
for (Integer i = 0; i < dictionary->count; i++) {
keysArray->array[i] = dictionary->items[i].key;
}
return keysArray;
}
// Function to get all objects in the dictionary
CCArray* dictionaryAllObjects(CCDictionary* dictionary) {
if (!dictionary) return NULL;
CCArray* objectsArray = (CCArray*)cc_safe_alloc(1, sizeof(CCArray));
objectsArray->type = CCType_Array;
if (!objectsArray) return NULL;
objectsArray->count = dictionary->count;
objectsArray->array = (void**)cc_safe_alloc(1, dictionary->count * sizeof(void*));
if (!objectsArray->array) {
free(objectsArray);
return NULL;
}
for (Integer i = 0; i < dictionary->count; i++) {
objectsArray->array[i] = dictionary->items[i].value;
}
return objectsArray;
}
CCDictionary* copyCCDictionary(const CCDictionary* original) {
if (!original) return NULL;
CCDictionary* copy = (CCDictionary*)cc_safe_alloc(1, sizeof(CCDictionary));
if (!copy) return NULL;
copy->type = original->type;
copy->count = original->count;
copy->items = (CCKeyValuePair*)cc_safe_alloc(1, copy->count * sizeof(CCKeyValuePair));
if (!copy->items) {
free(copy);
return NULL;
}
for (int i = 0; i < original->count; i++) {
copy->items[i].key = copyCCString(original->items[i].key);
copy->items[i].value = copyElement(original->items[i].value);
if (!copy->items[i].key || !copy->items[i].value) {
// Handle error: free allocated resources
for (int j = 0; j < i; j++) {
freeCCString(copy->items[j].key);
freeElement(copy->items[j].value);
}
free(copy->items);
free(copy);
return NULL;
}
}
return copy;
}
void freeCCDictionary(CCDictionary* dict) {
if (!dict) return;
// Free each key-value pair
if (dict->items) {
for (int i = 0; i < dict->count; i++) {
if (dict->items[i].key) {
freeCCString(dict->items[i].key);
}
if (dict->items[i].value) {
freeElement(dict->items[i].value);
}
}
free(dict->items);
}
// Free the CCDictionary struct itself
free(dict);
}
//Sort Descriptor Functions
CCSortDescriptor* sortDescriptor(void) {
CCSortDescriptor* newSortDescriptor = (CCSortDescriptor*)cc_safe_alloc(1, sizeof(CCSortDescriptor));
if (!newSortDescriptor) return NULL;
newSortDescriptor->type = CCType_SortDescriptor;
return newSortDescriptor;
}
CCSortDescriptor* sortDescriptorWithKey(CCString* key, bool ascending) {
CCSortDescriptor* newSortDescriptor = (CCSortDescriptor*)cc_safe_alloc(1, sizeof(CCSortDescriptor));
if (!newSortDescriptor) return NULL;
newSortDescriptor->type = CCType_SortDescriptor;
newSortDescriptor->key = key;
newSortDescriptor->ascending = ascending;
return newSortDescriptor;
}
// Quick Sort function for CCArray
void quickSort(CCArray* array, int low, int high, CCSortDescriptor* sortDescriptor) {
if (low < high) {
// Partition the array
int pi = partition(array, low, high, sortDescriptor);
// Recursively sort the two halves
quickSort(array, low, pi - 1, sortDescriptor);
quickSort(array, pi + 1, high, sortDescriptor);
}
}
int partition(CCArray* array, int low, int high, CCSortDescriptor* sortDescriptor) {
void* pivot = array->array[high];
CCDictionary* pivotDict = (CCDictionary*) pivot;
void* pivotValue = dictionaryObjectForKey(pivotDict, sortDescriptor->key);
int i = low - 1;
for (int j = low; j < high; j++) {
CCDictionary* currentDict = (CCDictionary*) array->array[j];
void* currentValue = dictionaryObjectForKey(currentDict, sortDescriptor->key);
if (compareDictionaryValues(currentValue, pivotValue, sortDescriptor->ascending) < 0) {
i++;
swap(array->array, i, j);
}
}
swap(array->array, i + 1, high);
return i + 1;
}
int compareDictionaryValues(void* value1, void* value2, bool ascending) {
// Compare as strings for demonstration purposes
CCType type = ((CCNull*)value1)->type;
if (type == CCType_String) {
const char* str1 = cStringOfString(value1);
const char* str2 = cStringOfString(value2);
int cmpResult = strcmp(str1, str2);
return ascending ? cmpResult : -cmpResult;
}
else if (type == CCType_Number) {
double num1 = numberDoubleValue(value1);
double num2 = numberDoubleValue(value2);
return compareNumberValues(num1, num2, ascending);
}
else if (type == CCType_Date) {
double num1 = ((CCDate*)value1)->timeValue;
double num2 = ((CCDate*)value2)->timeValue;
return compareNumberValues(num1, num2, ascending);
}
else if (type == CCType_Data) {
double num1 = (double)(((CCData*)value1)->length);
double num2 = (double)(((CCData*)value2)->length);
return compareNumberValues(num1, num2, ascending);
}
return 0;
}
int compareNumberValues(double num1, double num2, bool ascending) {
int comparisonResult;
if (num1 < num2) {
comparisonResult = -1;
} else if (num1 > num2) {
comparisonResult = 1;
} else {
comparisonResult = 0;
}
return ascending ? comparisonResult : -comparisonResult;
}
// Utility function to swap two elements in an array
void swap(void** array, int i, int j) {
void* temp = array[i];
array[i] = array[j];
array[j] = temp;
}
CCArray* sortedArrayUsingSortDescriptor(CCArray* array, CCSortDescriptor *sortDescriptor) {
if (!array || !sortDescriptor || array->count <= 1) return array;
// Create a copy of the array for the sorted result
CCArray* sortedArray = arrayWithArray(array);
// Perform Quick Sort
quickSort(sortedArray, 0, sortedArray->count - 1, sortDescriptor);
return sortedArray;
}
CCSortDescriptor* copyCCSortDescriptor(const CCSortDescriptor* original) {
if (!original) return NULL; // Handle NULL input safely
// Allocate memory for the new CCSortDescriptor
CCSortDescriptor* copy = (CCSortDescriptor*)cc_safe_alloc(1, sizeof(CCSortDescriptor));
if (!copy) return NULL; // Check allocation success
// Copy simple fields
copy->type = original->type;
copy->ascending = original->ascending;
// Deep copy the key (assuming CCString has a proper copy mechanism)
copy->key = copyCCString(original->key);
if (!copy->key) {
free(copy); // Clean up if copying key fails
return NULL;
}
return copy;
}
void freeCCSortDescriptor(CCSortDescriptor* descriptor) {
if (!descriptor) return; // Handle null input safely
// Free the key if it exists
if (descriptor->key) {
freeCCString(descriptor->key);
}
// Free the descriptor struct itself
free(descriptor);
}
//Regular Expression Functions
CCRegularExpression* regularExpression(void) {
CCRegularExpression* newRegularExpression = (CCRegularExpression*)cc_safe_alloc(1, sizeof(CCRegularExpression));
if (!newRegularExpression) return NULL;
newRegularExpression->type = CCType_RegularExpression;
return newRegularExpression;
}
CCRegularExpression* regularExpressionWithPattern(CCString* pattern, CCRegularExpressionOptions options) {
CCRegularExpression* newRegularExpression = (CCRegularExpression*)cc_safe_alloc(1, sizeof(CCRegularExpression));
if (!newRegularExpression) return NULL;
newRegularExpression->type = CCType_RegularExpression;
newRegularExpression->pattern = pattern;
newRegularExpression->options = options;
return newRegularExpression;
}
int convertOptionsToPOSIXFlags(CCRegularExpressionOptions options) {
int flags = REG_EXTENDED; // Use extended regex syntax
if (options & CCRegularExpressionCaseInsensitive) {
flags |= REG_ICASE;
}
// Other options like multiline matching may need additional handling outside of POSIX
return flags;
}
CCArray* matchesInString(CCRegularExpression* regex, CCString* string, CCRange* range) {
regex_t preg;
regmatch_t pmatch[1];
int posixFlags = convertOptionsToPOSIXFlags(regex->options);
const char* pattern = cStringOfString(regex->pattern);
const char* targetString = cStringOfString(string) + range->loc;
if (regcomp(&preg, pattern, posixFlags) != 0) {
perror("Regex compilation failed");
return NULL;
}
CCArray* resultsArray = array(); // Initializes a CCArray
unsigned long start = range->loc;
long long matchLength = 0;
while (regexec(&preg, targetString, 1, pmatch, 0) == 0) {
matchLength = pmatch[0].rm_eo - pmatch[0].rm_so;
CCRange* matchRange = ccRange(start + pmatch[0].rm_so, matchLength);
arrayAddObject(resultsArray, matchRange);
// Move past this match for the next search iteration
targetString += pmatch[0].rm_eo;
start += pmatch[0].rm_eo;
}
regfree(&preg);
return resultsArray;
}
//Data Functions
// Function to create and return an empty CCData
CCData* data(void) {
CCData* newData = (CCData*)cc_safe_alloc(1, sizeof(CCData));
newData->type = CCType_Data;
if (!newData) {
return NULL;
}
newData->bytes = NULL;
newData->length = 0;
return newData;
}
// Function to create a new CCData by copying an existing CCData
CCData* dataWithData(CCData* data) {
if (!data) {
return NULL;
}
CCData* newData = (CCData*)cc_safe_alloc(1, sizeof(CCData));
newData->type = CCType_Data;
if (!newData) {
return NULL;
}
newData->length = data->length;
if (newData->length > 0) {
newData->bytes = cc_safe_alloc(1, newData->length);
if (!newData->bytes) {
free(newData);
return NULL;
}
memcpy(newData->bytes, data->bytes, newData->length);
} else {
newData->bytes = NULL;
}
return newData;
}
// Function to create a CCData with a given bytes buffer
CCData* dataWithBytes(void* bytes, UInteger length) {
if (!bytes || length == 0) {
return data(); // Create an empty data object
}
CCData* newData = (CCData*)cc_safe_alloc(1, sizeof(CCData));
newData->type = CCType_Data;
if (!newData) {
return NULL;
}
newData->length = length;
newData->bytes = cc_safe_alloc(1, length);
if (!newData->bytes) {
free(newData);
return NULL;
}
memcpy(newData->bytes, bytes, length);
return newData;
}
// Function to check if two CCData objects are equal
bool dataIsEqualToData(CCData* data, CCData* data1) {
if (!data || !data1 || data->length != data1->length) {
return false;
}
if (data->length == 0) { // Both are empty and equal
return true;
}
return memcmp(data->bytes, data1->bytes, data->length) == 0;
}
// Function to read the contents of a file into a CCData structure
CCData* dataWithContentsOfFile(CCString* filePath) {
if (!filePath || !filePath->string) {
return NULL; // Handle invalid input
}
FILE *file = fopen(filePath->string, "rb");
if (!file) {
perror(cStringOfString(stringWithFormat("Failed to open file: %s", cStringOfString(filePath))));
return NULL;
}
// Determine the size of the file
fseek(file, 0, SEEK_END);
long fileSize = ftell(file);
rewind(file);
if (fileSize < 0) {
fclose(file);
return NULL;
}
// Allocate memory for CCData
CCData* data = (CCData*)cc_safe_alloc(1, sizeof(CCData));
data->type = CCType_Data;
if (!data) {
fclose(file);
return NULL;
}
data->bytes = cc_safe_alloc(1, fileSize);
if (!data->bytes) {
free(data);
fclose(file);
return NULL;
}
// Read file contents into the buffer
if (fread(data->bytes, 1, fileSize, file) != (size_t)fileSize) {
free(data->bytes);
free(data);
fclose(file);
return NULL;
}
data->length = (UInteger)fileSize;
fclose(file);
return data;
}
int writeDataToFile(const char* path, void* data, size_t dataSize) {
FILE *file = fopen(path, "wb");
if (!file) {
perror("Failed to open file");
return -1;
}
size_t written = fwrite(data, 1, dataSize, file);
if (written != dataSize) {
perror("Failed to write entire data");
fclose(file);
return -1;
}
fclose(file);
return 0;
}
bool dataWriteToFile(CCData* data, CCString* path) {
int write = writeDataToFile(cStringOfString(path), data->bytes, data->length);
return (write == -1) ? false : true;
}
CCData* copyCCData(const CCData* original) {
if (!original) return NULL;
CCData* copy = (CCData*)cc_safe_alloc(1, sizeof(CCData));
if (!copy) return NULL;
copy->type = original->type;
copy->length = original->length;
if (original->bytes && original->length > 0) {
copy->bytes = cc_safe_alloc(1, original->length);
if (!copy->bytes) {
free(copy); // Free the struct if byte allocation fails
return NULL;
}
// Copy the byte data
memcpy(copy->bytes, original->bytes, original->length);
} else {
copy->bytes = NULL;
}
return copy;
}
// Free function for CCData
void freeCCData(CCData* data) {
if (data) {
free(data->bytes);
free(data);
}
}
//Null Functions
CCNull* null() {
CCNull* newNull = (CCNull*)cc_safe_alloc(1, sizeof(CCNull));
newNull->type = CCType_Null;
if (!newNull) return NULL;
return newNull;
}
void* cc_safe_realloc(void* ptr, size_t new_size) {
// 1. Attempt to realloc while keeping SPIRAM preference
void* new_ptr = heap_caps_realloc(ptr, new_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
// 2. Fallback: If SPIRAM fails (or system doesn't support caps_realloc well),
// try standard realloc as a last resort.
if (!new_ptr && new_size > 0) {
new_ptr = realloc(ptr, new_size);
}
return new_ptr;
}
// Add this helper at the top of ObjectiveCC.c
void* cc_safe_alloc(size_t count, size_t size) {
// Force allocation in SPIRAM (PSRAM)
// MALLOC_CAP_SPIRAM: Use external RAM
// MALLOC_CAP_8BIT: Byte-addressable (required for structs)
void* ptr = heap_caps_calloc(count, size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
// Fallback: If SPIRAM fails, try Internal (though unlikely with 8MB)
if (!ptr) {
ptr = calloc(count, size);
}
return ptr;
}
// NOW: Find-and-Replace in ObjectiveCC.c:
// Replace 'calloc(1, sizeof(CCView))' with 'cc_safe_alloc(1, sizeof(CCView))'
// Replace 'calloc(1, sizeof(CCLabel))' with 'cc_safe_alloc(1, sizeof(CCLabel))'
// Replace 'cc_safe_alloc(1, size)' for strings with 'heap_caps_cc_safe_alloc(1, size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT)'
//JSON Functions
CCJSONObject* jsonObject(void) {
CCJSONObject* newJsonObject = (CCJSONObject*)cc_safe_alloc(1, sizeof(CCJSONObject));
if (!newJsonObject) return NULL;
newJsonObject->type = CCType_JSONObject;
return newJsonObject;
}
CCJSONObject* jsonObjectWithJSONString(CCString* string) {
CCJSONObject* newJsonObject = (CCJSONObject*)cc_safe_alloc(1, sizeof(CCJSONObject));
if (!newJsonObject) return NULL;
newJsonObject->type = CCType_JSONObject;
newJsonObject->jsonString = string;
return newJsonObject;
}
CCJSONObject* jsonObjectWithObject(void* object) {
CCJSONObject* newJsonObject = (CCJSONObject*)cc_safe_alloc(1, sizeof(CCJSONObject));
if (!newJsonObject) return NULL;
newJsonObject->type = CCType_JSONObject;
newJsonObject->jsonObject = object;
return newJsonObject;
}
CCString* stringForJsonObject(cJSON* jsonElement) {
return stringWithCString(jsonElement->valuestring);
}
CCNumber* numberForJsonObject(cJSON* jsonElement) {
return numberWithDouble(jsonElement->valuedouble);
}
CCDictionary* dictionaryForJsonObject(void* jsonObject1) {
cJSON* jsonObject = jsonObject1;
CCDictionary* newDictionary = dictionary();
cJSON *current_element = NULL;
cJSON_ArrayForEach(current_element, jsonObject) {
if (cJSON_IsString(current_element)) {
dictionarySetObjectForKey(newDictionary, stringForJsonObject(current_element), stringWithCString(current_element->string));
}
else if (cJSON_IsNumber(current_element)) {
dictionarySetObjectForKey(newDictionary, numberForJsonObject(current_element), stringWithCString(current_element->string));
}
else if (cJSON_IsObject(current_element)) {
dictionarySetObjectForKey(newDictionary, dictionaryForJsonObject(current_element), stringWithCString(current_element->string));
}
else if (cJSON_IsArray(current_element)) {
dictionarySetObjectForKey(newDictionary, arrayForJsonObject(current_element), stringWithCString(current_element->string));
}
else if (cJSON_IsNull(current_element)) {
dictionarySetObjectForKey(newDictionary, null(), stringWithCString(current_element->string));
}
}
return newDictionary;
}
CCArray* arrayForJsonObject(void* jsonObject1) {
cJSON* jsonObject = jsonObject1;
CCArray* newArray = array();
cJSON *current_element = NULL;
cJSON_ArrayForEach(current_element, jsonObject) {
if (cJSON_IsString(current_element)) {
arrayAddObject(newArray, stringForJsonObject(current_element));
}
else if (cJSON_IsNumber(current_element)) {
arrayAddObject(newArray, numberForJsonObject(current_element));
}
else if (cJSON_IsObject(current_element)) {
arrayAddObject(newArray, dictionaryForJsonObject(current_element));
}
else if (cJSON_IsArray(current_element)) {
arrayAddObject(newArray, arrayForJsonObject(current_element));
}
else if (cJSON_IsNull(current_element)) {
arrayAddObject(newArray, null());
}
}
return newArray;
}
void generateJsonStringFromObject(CCJSONObject* object, CCJSONWriteStyle writeStyle) {
CCType type = ((CCNull*)object->jsonObject)->type;
cJSON* jsonObject = NULL;
if (type == CCType_Array) {
jsonObject = cJsonArrayForArray(object->jsonObject);
}
else if (type == CCType_Dictionary) {
jsonObject = cJsonDictionaryForDictionary(object->jsonObject);
}
if (jsonObject != NULL) {
if (writeStyle == CCJSONWriteStyleReadable) {
char *prettyPrintedJson = cJSON_Print(jsonObject);
object->jsonString = stringWithCString(prettyPrintedJson);
}
else if (writeStyle == CCJSONWriteStyleCompressed) {
char *compressedJson = cJSON_PrintUnformatted(jsonObject);
object->jsonString = stringWithCString(compressedJson);
}
}
}
void* cJsonArrayForArray(CCArray* array) {
cJSON *children = cJSON_CreateArray();
for (int i = 0; i < array->count; i++) {
void* arrayObject = arrayObjectAtIndex(array, i);
CCType type = ((CCNull*)arrayObject)->type;
if (type == CCType_String) {
CCString* string = (CCString*)arrayObject;
cJSON_AddItemToArray(children, cJSON_CreateString(cStringOfString(string)));
}
else if (type == CCType_Number) {
CCNumber* number = (CCNumber*)arrayObject;
cJSON_AddItemToArray(children, cJSON_CreateNumber(numberDoubleValue(number)));
}
else if (type == CCType_Null) {
cJSON_AddItemToArray(children, cJSON_CreateNull());
}
else if (type == CCType_Array) {
CCArray* array1 = (CCArray*)arrayObject;
cJSON_AddItemToArray(children, cJsonArrayForArray(array1));
}
else if (type == CCType_Dictionary) {
CCDictionary* dictionary = (CCDictionary*)arrayObject;
cJSON_AddItemToArray(children, cJsonDictionaryForDictionary(dictionary));
}
}
return children;
}
void* cJsonDictionaryForDictionary(CCDictionary* dictionary) {
cJSON* object = cJSON_CreateObject();
CCArray* allKeys = dictionaryAllKeys(dictionary);
for (int i = 0; i < allKeys->count; i++) {
CCString* key = arrayObjectAtIndex(allKeys, i);
void* dictObject = dictionaryObjectForKey(dictionary, key);
CCType dictObjectType = ((CCNull*)dictObject)->type;
if (dictObjectType == CCType_String) {
cJSON_AddStringToObject(object, cStringOfString(key), cStringOfString(dictObject));
}
else if (dictObjectType == CCType_Number) {
cJSON_AddNumberToObject(object, cStringOfString(key), numberDoubleValue(dictObject));
}
else if (dictObjectType == CCType_Null) {
cJSON_AddNullToObject(object, cStringOfString(key));
}
else if (dictObjectType == CCType_Array) {
cJSON_AddItemToObject(object, cStringOfString(key), cJsonArrayForArray(dictObject));
}
else if (dictObjectType == CCType_Dictionary) {
cJSON_AddItemToObject(object, cStringOfString(key), cJsonDictionaryForDictionary(dictObject));
}
}
return object;
}
void generateObjectFromJsonString(CCJSONObject* object) {
CCString* string = object->jsonString;
cJSON *root = cJSON_Parse(cStringOfString(string));
if (root == NULL) {
fprintf(stderr, "Error parsing JSON data\n");
return;
}
if (cJSON_IsObject(root)) {
object->jsonObject = dictionaryForJsonObject(root);
}
else if (cJSON_IsArray(root)) {
object->jsonObject = arrayForJsonObject(root);
}
}
//Time Functions
double getCurrentTimeInSecondsSinceEpoch(void) {
struct timeval tv;
gettimeofday(&tv, NULL);
// Convert seconds and microseconds into a total number of seconds
// with millisecond precision as a decimal part
double secondsSinceEpoch = (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
return secondsSinceEpoch;
}
void getCurrentDateTime(int *year, int *month, int *day, int *hour, int *minute, int *second, int *millisecond) {
// Step 1: Get the current time in seconds since the Unix epoch
time_t current_time = time(NULL);
// Step 2: Convert to local time structure
struct tm *local_time = localtime(¤t_time);
// Assign date and time components to respective variables
*year = local_time->tm_year + 1900; // tm_year is years since 1900
*month = local_time->tm_mon + 1; // tm_mon is months since January (0-11)
*day = local_time->tm_mday;
*hour = local_time->tm_hour;
*minute = local_time->tm_min;
*second = local_time->tm_sec;
// Step 3: Get current time with milliseconds using gettimeofday
struct timeval tv;
gettimeofday(&tv, NULL);
*millisecond = tv.tv_usec / 1000; // Convert microseconds to milliseconds
}
//Date Functions
CCDate* date(void) {
CCDate* newDate = (CCDate*)cc_safe_alloc(1, sizeof(CCDate));
if (!newDate) return NULL;
newDate->type = CCType_Date;
newDate->timeValue = getCurrentTimeInSecondsSinceEpoch();
return newDate;
}
CCDate* dateWithTimeInterval(double timeInterval) {
CCDate* newDate = (CCDate*)cc_safe_alloc(1, sizeof(CCDate));
if (!newDate) return NULL;
newDate->type = CCType_Date;
newDate->timeValue = timeInterval;
return newDate;
}
void dateAddTimeInterval(CCDate* date, double timeInterval) {
date->timeValue += timeInterval;
}
bool dateEarlierThanDate(CCDate* date, CCDate* date1) {
return (date->timeValue < date1->timeValue);
}
bool dateLaterThanDate(CCDate* date, CCDate* date1) {
return (date->timeValue > date1->timeValue);
}
bool dateEqualToDate(CCDate* date, CCDate* date1) {
return (date->timeValue == date1->timeValue);
}
// Function to copy a CCDate
CCDate* copyCCDate(const CCDate* original) {
if (!original) return NULL;
CCDate* copy = (CCDate*)cc_safe_alloc(1, sizeof(CCDate));
if (!copy) return NULL;
copy->type = original->type;
copy->timeValue = original->timeValue;
return copy;
}
void freeCCDate(CCDate* date) {
if (date) {
free(date);
}
}
CCDateFormatter* dateFormatter(void) {
// 1. Use calloc instead of malloc.
// This ensures all pointers (like dateFormat) start as NULL
// and all integers/enums start as 0.
CCDateFormatter* newDateFormatter = (CCDateFormatter*)cc_safe_alloc(1, sizeof(CCDateFormatter));
if (!newDateFormatter) return NULL;
newDateFormatter->type = CCType_DateFormatter;
// 2. Initialize required children
newDateFormatter->calendar = calendar();
newDateFormatter->locale = locale();
// Explicitly unnecessary if using calloc, but good for documentation:
newDateFormatter->dateFormat = NULL;
newDateFormatter->timeZone = NULL;
return newDateFormatter;
}
// Helper to convert struct tm to time_t treating the tm as UTC
// This is standard in BSD/GNU but might need manual implementation on some embedded systems.
// If your environment has timegm(), use that. Otherwise, this logic works.
time_t _timegm_custom(struct tm *tm) {
time_t ret;
char *tz;
// Save current TZ
tz = getenv("TZ");
// Set TZ to UTC
setenv("TZ", "", 1);
tzset();
// Convert
ret = mktime(tm);
// Restore TZ
if (tz) setenv("TZ", tz, 1);
else unsetenv("TZ");
tzset();
return ret;
}
// ------------------------------------------------------------
// Date Formatter Functions
// ------------------------------------------------------------
// Internal helper: Map Date Style to Format String
const char* _getFormatStringForDateStyle(CCDateFormatterStyle style) {
switch (style) {
case CCDateFormatterStyleShort:
return "%m/%d/%y"; // e.g. 12/31/25
case CCDateFormatterStyleMedium:
return "%b %d, %Y"; // e.g. Dec 31, 2025
case CCDateFormatterStyleLong:
return "%B %d, %Y"; // e.g. December 31, 2025
default:
return ""; // No date component
}
}
// Internal helper: Map Time Style to Format String
const char* _getFormatStringForTimeStyle(CCDateFormatterStyle style) {
switch (style) {
case CCDateFormatterStyleShort:
return "%H:%M"; // e.g. 14:30
case CCDateFormatterStyleMedium:
return "%H:%M:%S"; // e.g. 14:30:59
case CCDateFormatterStyleLong:
return "%H:%M:%S %Z"; // e.g. 14:30:59 EST
default:
return ""; // No time component
}
}
CCDate* dateFromString(CCDateFormatter* dateFormatter, CCString* string) {
if (!dateFormatter || !string) return NULL;
CCDate* newDate = (CCDate*)cc_safe_alloc(1, sizeof(CCDate));
if (!newDate) return NULL;
newDate->type = CCType_Date;
// 1. Determine Format String
char formatBuffer[64];
const char* finalFormatStr;
if (dateFormatter->dateFormat != NULL) {
finalFormatStr = cStringOfString(dateFormatter->dateFormat);
} else {
// Generate from styles
const char* dStr = _getFormatStringForDateStyle(dateFormatter->dateStyle);
const char* tStr = _getFormatStringForTimeStyle(dateFormatter->timeStyle);
if (strlen(dStr) > 0 && strlen(tStr) > 0) {
snprintf(formatBuffer, sizeof(formatBuffer), "%s %s", dStr, tStr);
} else {
snprintf(formatBuffer, sizeof(formatBuffer), "%s%s", dStr, tStr);
}
finalFormatStr = formatBuffer;
}
// 2. Parse using strptime
struct tm tm_struct;
memset(&tm_struct, 0, sizeof(struct tm));
const char* inputStr = cStringOfString(string);
if (strptime(inputStr, finalFormatStr, &tm_struct) == NULL) {
free(newDate);
return NULL;
}
// 3. Convert to time_t (UTC)
time_t rawTime = _timegm_custom(&tm_struct);
// 4. Remove TimeZone Offset
if (dateFormatter->timeZone) {
rawTime -= (long)dateFormatter->timeZone->secondsFromGmt;
}
newDate->timeValue = (double)rawTime;
return newDate;
}
CCString* stringFromDate(CCDateFormatter* dateFormatter, CCDate* date) {
if (!dateFormatter || !date) return NULL;
// 1. Determine Format String
// If the user manually set 'dateFormat', use it.
// Otherwise, generate it from the DateStyle and TimeStyle.
char formatBuffer[64];
const char* finalFormatStr;
if (dateFormatter->dateFormat != NULL) {
finalFormatStr = cStringOfString(dateFormatter->dateFormat);
} else {
// Generate format from Styles
const char* dStr = _getFormatStringForDateStyle(dateFormatter->dateStyle);
const char* tStr = _getFormatStringForTimeStyle(dateFormatter->timeStyle);
// Combine them.
// Logic: If we have both, put a space in between.
if (strlen(dStr) > 0 && strlen(tStr) > 0) {
snprintf(formatBuffer, sizeof(formatBuffer), "%s %s", dStr, tStr);
} else {
snprintf(formatBuffer, sizeof(formatBuffer), "%s%s", dStr, tStr);
}
finalFormatStr = formatBuffer;
}
// 2. Get UTC Timestamp
time_t rawTime = (time_t)date->timeValue;
// 3. Apply TimeZone Offset
if (dateFormatter->timeZone) {
rawTime += (long)dateFormatter->timeZone->secondsFromGmt;
}
// 4. Format
struct tm *timeInfo = gmtime(&rawTime);
char outputBuffer[128];
if (strftime(outputBuffer, sizeof(outputBuffer), finalFormatStr, timeInfo) == 0) {
return NULL;
}
return stringWithCString(outputBuffer);
}
// ------------------------------------------------------------
// Calendar Functions (Completed)
// ------------------------------------------------------------
CCCalendar* calendar(void) {
CCCalendar* newCalendar = (CCCalendar*)cc_safe_alloc(1, sizeof(CCCalendar));
if (!newCalendar) return NULL;
newCalendar->type = CCType_Calendar;
newCalendar->identifier = CCCalendarIdentifierGregorian; // Default
newCalendar->locale = locale();
newCalendar->timeZone = timeZone();
return newCalendar;
}
CCCalendar* calendarWithIdentifier(CCCalendarIdentifier identifier) {
CCCalendar* newCalendar = (CCCalendar*)cc_safe_alloc(1, sizeof(CCCalendar));
if (!newCalendar) return NULL;
newCalendar->type = CCType_Calendar;
newCalendar->identifier = identifier;
newCalendar->locale = locale();
newCalendar->timeZone = timeZone();
return newCalendar;
}
// ------------------------------------------------------------
// Component Logic (Completed)
// ------------------------------------------------------------
CCDateComponents* componentsFromDate(CCDate* date) {
if (!date) return NULL;
CCDateComponents* comps = (CCDateComponents*)cc_safe_alloc(1, sizeof(CCDateComponents));
if (!comps) return NULL;
comps->type = CCType_DateComponents;
// Default dependencies
comps->calendar = calendar();
comps->timeZone = timeZone();
comps->date = date; // Keep reference if needed, or copy
// 1. Apply Timezone Offset
time_t rawTime = (time_t)date->timeValue;
if (comps->timeZone) {
rawTime += (long)comps->timeZone->secondsFromGmt;
}
// 2. Break down into components
struct tm *tm_val = gmtime(&rawTime);
// 3. Map struct tm to CCDateComponents
comps->year = tm_val->tm_year + 1900;
comps->month = tm_val->tm_mon + 1; // tm_mon is 0-11
comps->day = tm_val->tm_mday;
comps->hour = tm_val->tm_hour;
comps->minute = tm_val->tm_min;
comps->second = tm_val->tm_sec;
comps->weekday = tm_val->tm_wday + 1; // Typically 1-7 in high level frameworks
comps->yearForWeekOfYear = tm_val->tm_year + 1900; // Simplified
// Calculated fields not in struct tm directly
comps->era = 1; // Simplified Gregorian AD
comps->quarter = (comps->month - 1) / 3 + 1;
return comps;
}
CCDate* dateFromComponents(CCDateComponents* components) {
if (!components) return NULL;
CCDate* newDate = (CCDate*)cc_safe_alloc(1, sizeof(CCDate));
if (!newDate) return NULL;
newDate->type = CCType_Date;
// 1. Map components to struct tm
struct tm tm_val;
memset(&tm_val, 0, sizeof(struct tm));
tm_val.tm_year = components->year - 1900;
tm_val.tm_mon = components->month - 1; // 0-11
tm_val.tm_mday = components->day;
tm_val.tm_hour = components->hour;
tm_val.tm_min = components->minute;
tm_val.tm_sec = components->second;
tm_val.tm_isdst = -1; // Let system determine DST if possible, or 0
// 2. Convert to timestamp (Treating as UTC first)
time_t rawTime = _timegm_custom(&tm_val);
// 3. Remove TimeZone Offset to get back to absolute UTC
if (components->timeZone) {
rawTime -= (long)components->timeZone->secondsFromGmt;
}
newDate->timeValue = (double)rawTime;
return newDate;
}
Integer dateComponentValueForDate(CCDate* date, CCCalendarUnit calendarUnit) {
return 0;
}
//Date Components Functions
void dateComponentsSetValueForComponent(CCDateComponents* dateComponents, Integer value, CCCalendarUnit component) {
}
Integer dateComponentsValueForComponent(CCDateComponents* dateComponents, CCCalendarUnit component) {
return 0;
}
//Time Zone Functions
CCTimeZone* timeZone(void) {
if (programTimeZone != NULL) {
return programTimeZone;
}
programTimeZone = timeZoneWithName(programTimeZoneIdentifier);
return programTimeZone;
}
CCTimeZone* timeZoneWithName(CCString* name) {
CCTimeZone* newTimeZone = (CCTimeZone*)cc_safe_alloc(1, sizeof(CCTimeZone));
if (!newTimeZone) return NULL;
newTimeZone->type = CCType_TimeZone;
newTimeZone->name = ccs("America / New York");
newTimeZone->secondsFromGmt = -18000;
newTimeZone->abbreviation = ccs("GMT");
/*CCString* timeZoneFilePath = ccs("/spiflash/timeZoneData.json");
CCJSONObject* timeZoneFile = jsonObjectWithJSONString(stringWithContentsOfFile(timeZoneFilePath));
generateObjectFromJsonString(timeZoneFile);
CCArray* timeZoneItems = (CCArray*)timeZoneFile->jsonObject;
for (int i = 0; i < timeZoneItems->count; i++) {
CCDictionary* timeZoneItem = arrayObjectAtIndex(timeZoneItems, i);
CCString *timeZoneName = dictionaryObjectForKey(timeZoneItem, ccs("name"));
if (stringEqualsString(name, timeZoneName)) {
CCString* dictDescription = objectDescription(timeZoneItem);
printf("\n\Time Zone Item Description\n\n%s\n\n", cStringOfString(dictDescription));
newTimeZone->name = dictionaryObjectForKey(timeZoneItem, ccs("name"));
newTimeZone->secondsFromGmt = numberDoubleValue(dictionaryObjectForKey(timeZoneItem, ccs("secondsFromGmt")));
newTimeZone->abbreviation = dictionaryObjectForKey(timeZoneItem, ccs("abbreviation"));
}
}*/
return newTimeZone;
}
CCArray* timeZoneNames(void) {
CCString* timeZoneNamesPath = ccs("/spiflash/timeZoneNames.txt");
CCString* timeZoneStrings = stringWithContentsOfFile(timeZoneNamesPath);
CCArray* timeZoneNames = stringComponentsSeparatedByString(timeZoneStrings, ccs(","));
return timeZoneNames;
}
CCTimeZone* systemTimeZone(void) {
//Use C function to get the current time zone
//return a CCTimeZone with the member values set
//to whatever data the C functions return
return NULL;
}
//Locale Functions
CCLocale* locale(void) {
if (programLocale != NULL) {
return programLocale;
}
programLocale = localeWithIdentifier(programLocaleIdentifier);
return programLocale;
}
CCLocale* localeWithIdentifier(CCString* identifier) {
CCLocale* newLocale = (CCLocale*)cc_safe_alloc(1, sizeof(CCLocale));
if (!newLocale) return NULL;
newLocale->type = CCType_Locale;
//CCString* localeFilePath = resourceFilePath(ccs("localeData.json"));
CCString* localeFilePath = ccs("/spiflash/localeData.json");
printf("\nlocaleFilePath %s\n", cStringOfString(localeFilePath));
CCJSONObject* localeFile = jsonObjectWithJSONString(stringWithContentsOfFile(localeFilePath));
generateObjectFromJsonString(localeFile);
CCArray* localeItems = (CCArray*)localeFile->jsonObject;
printf("\nlocaleItems: %d\n", localeItems->count);
for (int i = 0; i < localeItems->count; i++) {
CCDictionary* localeItem = arrayObjectAtIndex(localeItems, i);
CCString *localeIdentifier = dictionaryObjectForKey(localeItem, ccs("localeIdentifier"));
if (stringEqualsString(identifier, localeIdentifier)) {
CCString* dictDescription = objectDescription(localeItem);
printf("\n\nLocale Item Description\n\n%s\n\n", cStringOfString(dictDescription));
}
newLocale->alternateQuotationBeginDelimiter = dictionaryObjectForKey(localeItem, ccs("alternateQuotationBeginDelimiter"));
newLocale->alternateQuotationEndDelimiter = dictionaryObjectForKey(localeItem, ccs("alternateQuotationEndDelimiter"));
newLocale->countryCode = dictionaryObjectForKey(localeItem, ccs("countryCode"));
newLocale->quotationBeginDelimiter = dictionaryObjectForKey(localeItem, ccs("quotationBeginDelimiter"));
newLocale->quotationEndDelimiter = dictionaryObjectForKey(localeItem, ccs("quotationEndDelimiter"));
newLocale->languageCode = dictionaryObjectForKey(localeItem, ccs("languageCode"));
newLocale->groupingSeparator = dictionaryObjectForKey(localeItem, ccs("groupingSeparator"));
newLocale->currencySymbol = dictionaryObjectForKey(localeItem, ccs("currencySymbol"));
newLocale->localeIdentifier = dictionaryObjectForKey(localeItem, ccs("localeIdentifier"));
newLocale->collatorIdentifier = dictionaryObjectForKey(localeItem, ccs("collatorIdentifier"));
newLocale->collationIdentifier = dictionaryObjectForKey(localeItem, ccs("collationIdentifier"));
newLocale->decimalSeparator = dictionaryObjectForKey(localeItem, ccs("decimalSeparator"));
newLocale->calendarIdentifier = dictionaryObjectForKey(localeItem, ccs("calendarIdentifier"));
newLocale->currencyCode = dictionaryObjectForKey(localeItem, ccs("currencyCode"));
}
return newLocale;
}
CCArray* localeIdentifiers(void) {
CCString* localeIdentifiersPath = ccs("/spiflash/localeIdentifiers.txt");
CCString* localeIdentifiersStrings = stringWithContentsOfFile(localeIdentifiersPath);
CCArray* localeIdentifiers = stringComponentsSeparatedByString(localeIdentifiersStrings, ccs(","));
return localeIdentifiers;
}
//Log Functions
void ccLog(const char* format, ...) {
if (format == NULL) {
return;
}
va_list args;
va_start(args, format);
int size = vsnprintf(NULL, 0, format, args) + 1; // Determine the size needed
va_end(args);
va_start(args, format);
char* buffer = (char*)cc_safe_alloc(1, size);
if (!buffer) {
va_end(args);
return;
}
vsnprintf(buffer, size, format, args);
va_end(args);
CCString* formattedString = (CCString*)cc_safe_alloc(1, sizeof(CCString));
formattedString->type = CCType_String;
if (!formattedString) {
free(buffer);
return;
}
formattedString->length = size - 1; // Exclude null terminator
formattedString->string = buffer;
ccLogString(formattedString);
freeCCString(formattedString);
}
void ccLogString(CCString* string) {
}
CCString* stringForType(CCType type) {
CCString* typeString = NULL;
if (type == CCType_Range) typeString = ccs("Range");
else if (type == CCType_Point) typeString = ccs("Point");
else if (type == CCType_Size) typeString = ccs("Size");
else if (type == CCType_Rect) typeString = ccs("Rect");
else if (type == CCType_StringEncoding) typeString = ccs("StringEncoding");
else if (type == CCType_String) typeString = ccs("String");
else if (type == CCType_Number) typeString = ccs("Number");
else if (type == CCType_Date) typeString = ccs("Date");
else if (type == CCType_DateFormatter) typeString = ccs("DateFormatter");
else if (type == CCType_Calendar) typeString = ccs("Calendar");
else if (type == CCType_DateComponents) typeString = ccs("DateComponents");
else if (type == CCType_TimeZone) typeString = ccs("TimeZone");
else if (type == CCType_Locale) typeString = ccs("Locale");
else if (type == CCType_Data) typeString = ccs("Data");
else if (type == CCType_Archiver) typeString = ccs("Archiver");
else if (type == CCType_Array) typeString = ccs("Array");
else if (type == CCType_KeyValuePair) typeString = ccs("KeyValuePair");
else if (type == CCType_Dictionary) typeString = ccs("Dictionary");
else if (type == CCType_SortDescriptor) typeString = ccs("SortDescriptor");
else if (type == CCType_RegularExpression) typeString = ccs("RegularExpression");
else if (type == CCType_JSONObject) typeString = ccs("JSONObject");
else if (type == CCType_Null) typeString = ccs("Null");
else if (type == CCType_Thread) typeString = ccs("Thread");
else if (type == CCType_URLResponse) typeString = ccs("URLResponse");
else if (type == CCType_URLRequest) typeString = ccs("URLRequest");
else if (type == CCType_SerialPort) typeString = ccs("SerialPort");
else if (type == CCType_Color) typeString = ccs("Color");
else if (type == CCType_Layer) typeString = ccs("Layer");
else if (type == CCType_View) typeString = ccs("View");
else if (type == CCType_Font) typeString = ccs("Font");
else if (type == CCType_Label) typeString = ccs("Label");
else if (type == CCType_PointPath) typeString = ccs("PointPath");
else if (type == CCType_ShapeLayer) typeString = ccs("ShapeLayer");
else if (type == CCType_Image) typeString = ccs("Image");
else if (type == CCType_ImageView) typeString = ccs("ImageView");
else if (type == CCType_GraphicsContext) typeString = ccs("GraphicsContext");
else if (type == CCType_Transform) typeString = ccs("Transform");
else if (type == CCType_Transform3D) typeString = ccs("Transform3D");
else if (type == CCType_GestureRecognizer) typeString = ccs("GestureRecognizer");
else typeString = ccs("Unknown Type");
return typeString;
}
CCString* objectDescription(void* object) {
CCString* descriptionString = string();
CCType type = ((CCNull*)object)->type;
if (type == CCType_String) {
CCString* string = ((CCString*)object);
descriptionString = string;
}
else if (type == CCType_Array) {
CCArray* array = ((CCArray*)object);
CCString* arrayString = string();
for (int i = 0; i < array->count; i++) {
void* arrayObject = arrayObjectAtIndex(array, i);
CCString* arrayObjectDescription = objectDescription(arrayObject);
arrayString = stringByAppendingFormat(arrayString, "\n%s,\n", cStringOfString(arrayObjectDescription));
}
descriptionString = arrayString;
}
else if (type == CCType_Dictionary) {
CCDictionary* dictionary = ((CCDictionary*)object);
CCString* dictionaryString = string();
CCArray* dictionaryKeys = dictionaryAllKeys(dictionary);
for (int i = 0; i < dictionaryKeys->count; i++) {
CCString* key = arrayObjectAtIndex(dictionaryKeys, i);
void* dictionaryObject = dictionaryObjectForKey(dictionary, key);
CCString* dictionaryObjectDescription = objectDescription(dictionaryObject);
dictionaryString = stringByAppendingFormat(dictionaryString, "\n%s: %s,\n", cStringOfString(key), cStringOfString(dictionaryObjectDescription));
}
descriptionString = dictionaryString;
}
else if (type == CCType_Number) {
CCNumber* number = ((CCNumber*)object);
CCString* numberString = stringWithFormat("%f", number->doubleValue);
descriptionString = numberString;
}
else if (type == CCType_Null) {
CCString* nullString = ccs("CCNull");
descriptionString = nullString;
}
else if (type == CCType_Rect) {
CCRect* rect = ((CCRect*)object);
CCString* rectString = stringWithFormat("X: %f, y: %f, width: %f, height: %f", rect->origin->x, rect->origin->y, rect->size->width, rect->size->height);
descriptionString = rectString;
}
else if (type == CCType_Size) {
CCSize* size = ((CCSize*)object);
CCString* sizeString = stringWithFormat("width: %f, height: %f", size->width, size->height);
descriptionString = sizeString;
}
else if (type == CCType_Point) {
CCPoint* point = ((CCPoint*)object);
CCString* pointString = stringWithFormat("X: %f, y: %f", point->x, point->y);
descriptionString = pointString;
}
else if (type == CCType_Data) {
CCData* data = ((CCData*)object);
CCString* dataString = stringWithFormat("Data: %ld Bytes", data->length);
descriptionString = dataString;
}
else if (type == CCType_Color) {
CCColor* color = ((CCColor*)object);
CCString* colorString = stringWithFormat("R: %f, G: %f, B: %f, A: %f", color->r, color->g, color->b, color->a);
descriptionString = colorString;
}
else if (type == CCType_Date) {
// 2. Setup Date Formatter
CCDateFormatter* fmt = dateFormatter();
fmt->dateStyle = CCDateFormatterStyleShort; // e.g., 12/31/25
fmt->timeStyle = CCDateFormatterStyleShort; // e.g., 14:30
CCDate* date = ((CCDate*)object);
CCString* dateString = stringFromDate(fmt, date);
descriptionString = dateString;
}
return descriptionString;
}