summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/candy_gear.cpp89
-rw-r--r--src/candy_gear.h33
-rw-r--r--src/color.c61
-rw-r--r--src/color.h40
-rw-r--r--src/core.cpp292
-rw-r--r--src/core.h97
-rw-r--r--src/font.c60
-rw-r--r--src/font.h40
-rw-r--r--src/graphic.c67
-rw-r--r--src/graphic.h33
-rw-r--r--src/key.c71
-rw-r--r--src/key.h33
-rw-r--r--src/loader.c162
-rw-r--r--src/loader.h159
-rw-r--r--src/log.c38
-rw-r--r--src/log.h33
-rw-r--r--src/main.cpp151
-rw-r--r--src/palette.c58
-rw-r--r--src/palette.h40
-rw-r--r--src/palette_implementation.cpp35
-rw-r--r--src/palette_implementation.h33
-rw-r--r--src/pgm_image.cpp70
-rw-r--r--src/pgm_image.h42
-rw-r--r--src/point.c122
-rw-r--r--src/point.h40
-rw-r--r--src/rect.c209
-rw-r--r--src/rect.h40
-rw-r--r--src/sound.c73
-rw-r--r--src/sound.h40
-rw-r--r--src/sprite.c198
-rw-r--r--src/sprite.h40
-rw-r--r--src/sprite_implementation.cpp58
-rw-r--r--src/sprite_implementation.h49
-rw-r--r--src/texture.c223
-rw-r--r--src/texture.h41
35 files changed, 2870 insertions, 0 deletions
diff --git a/src/candy_gear.cpp b/src/candy_gear.cpp
new file mode 100644
index 0000000..484ce6a
--- /dev/null
+++ b/src/candy_gear.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "candy_gear.h"
+
+#include <mruby/array.h>
+#include <mruby/hash.h>
+#include <mruby/string.h>
+
+#include <yaml-cpp/yaml.h>
+
+static mrb_value
+parse_node(mrb_state *mrb, const YAML::Node &node)
+{
+ mrb_value value;
+ std::string scalar;
+
+ switch(node.Type())
+ {
+ case YAML::NodeType::Null:
+ return mrb_nil_value();
+
+ case YAML::NodeType::Scalar:
+ scalar = node.as<std::string>();
+ return mrb_str_new(mrb, scalar.data(), scalar.size());
+
+ case YAML::NodeType::Sequence:
+ value = mrb_ary_new_capa(mrb, node.size());
+ for (YAML::const_iterator it = node.begin(); it != node.end(); it++)
+ mrb_ary_push(mrb, value, parse_node(mrb, *it));
+ return value;
+
+ case YAML::NodeType::Map:
+ value = mrb_hash_new_capa(mrb, node.size());
+ for(YAML::const_iterator it = node.begin(); it != node.end(); it++)
+ mrb_hash_set(
+ mrb, value, parse_node(mrb, it->first), parse_node(mrb, it->second));
+ return value;
+
+ case YAML::NodeType::Undefined:
+ default:
+ return mrb_nil_value();
+ }
+}
+
+static mrb_value
+cg_mCandyGear_load_yaml(mrb_state *mrb, mrb_value self)
+{
+ const char *file_path;
+
+ mrb_get_args(mrb, "z", &file_path);
+
+ YAML::Node root = YAML::LoadFile(file_path);
+
+ return parse_node(mrb, root);
+}
+
+static mrb_value
+cg_mCandyGear_quit(mrb_state *mrb, mrb_value self)
+{
+ cg_core.quit_game = SDL_TRUE;
+
+ return self;
+}
+
+void
+cg_candy_gear_init(mrb_state *mrb)
+{
+ struct RClass *cg_m;
+
+ cg_m = mrb_module_get(mrb, "CandyGear");
+ mrb_define_class_method(
+ mrb, cg_m, "load_yaml", cg_mCandyGear_load_yaml, MRB_ARGS_REQ(1));
+ mrb_define_class_method(
+ mrb, cg_m, "quit", cg_mCandyGear_quit, MRB_ARGS_NONE());
+}
diff --git a/src/candy_gear.h b/src/candy_gear.h
new file mode 100644
index 0000000..aca4740
--- /dev/null
+++ b/src/candy_gear.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CANDY_GEAR_GRAPHIC_H
+#define CANDY_GEAR_CANDY_GEAR_H 1
+
+#include "core.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void
+cg_candy_gear_init(mrb_state *mrb);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* CANDY_GEAR_CANDY_GEAR_H */
diff --git a/src/color.c b/src/color.c
new file mode 100644
index 0000000..0ab9392
--- /dev/null
+++ b/src/color.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "color.h"
+
+void
+cg_free_color(mrb_state *mrb, void* obj)
+{
+ struct cg_color *ptr = obj;
+
+ mrb_free(mrb, ptr);
+}
+
+const struct mrb_data_type cg_color_type = { "CG_Color", cg_free_color };
+
+static mrb_value
+cg_cColor_initialize(mrb_state *mrb, mrb_value self)
+{
+ mrb_int red, green, blue, alpha;
+ struct cg_color *ptr;
+
+ alpha = 0xff;
+ mrb_get_args(mrb, "iii|i", &red, &green, &blue, &alpha);
+ ptr = (struct cg_color *)DATA_PTR(self);
+ if(ptr) mrb_free(mrb, ptr);
+ ptr = (struct cg_color *)mrb_malloc(mrb, sizeof(struct cg_color));
+
+ ptr->data.r = red;
+ ptr->data.g = green;
+ ptr->data.b = blue;
+ ptr->data.a = alpha;
+
+ mrb_data_init(self, ptr, &cg_color_type);
+ return self;
+}
+
+void
+cg_color_init(mrb_state *mrb)
+{
+ struct RClass *cg_m, *cg_cColor;
+
+ cg_m = mrb_module_get(mrb, "CandyGear");
+ cg_cColor = mrb_define_class_under(mrb, cg_m, "Color", mrb->object_class);
+ MRB_SET_INSTANCE_TT(cg_cColor, MRB_TT_DATA);
+ mrb_define_method(
+ mrb, cg_cColor, "initialize", cg_cColor_initialize,
+ MRB_ARGS_REQ(3) | MRB_ARGS_OPT(1));
+}
diff --git a/src/color.h b/src/color.h
new file mode 100644
index 0000000..ee1b3ed
--- /dev/null
+++ b/src/color.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CANDY_GEAR_COLOR_H
+#define CANDY_GEAR_COLOR_H 1
+
+#include "core.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct cg_color
+{
+ SDL_Color data;
+};
+
+extern const struct mrb_data_type cg_color_type;
+
+void
+cg_color_init(mrb_state *mrb);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* CANDY_GEAR_COLOR_H */
diff --git a/src/core.cpp b/src/core.cpp
new file mode 100644
index 0000000..12e5b01
--- /dev/null
+++ b/src/core.cpp
@@ -0,0 +1,292 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "core.h"
+
+#include <yaml-cpp/yaml.h>
+
+/*
+ Stretches the image to fill the screen vertically, horizontally, or both;
+ without changing the game aspect ratio.
+ */
+void
+cg_Engine_calculate_full_scale()
+{
+ double screen_ratio, game_ratio, scale;
+
+ screen_ratio =
+ (double)cg_core.screen_width/(double)cg_core.screen_height;
+ game_ratio = (double)cg_core.game_width/(double)cg_core.game_height;
+
+ // If screen is proportionally taller than game.
+ if(screen_ratio < game_ratio)
+ {
+ scale = (double)cg_core.screen_width/(double)cg_core.game_width;
+
+ cg_core.screen_rect.w = cg_core.game_width * scale;
+ cg_core.screen_rect.h = cg_core.game_height * scale;
+ cg_core.screen_rect.x = 0;
+ cg_core.screen_rect.y = cg_core.screen_height/2 -
+ cg_core.screen_rect.h/2;
+ }
+ // If screen is proportionally wider than game.
+ else if(screen_ratio > game_ratio)
+ {
+ scale = (double)cg_core.screen_height/(double)cg_core.game_height;
+
+ cg_core.screen_rect.w = cg_core.game_width * scale;
+ cg_core.screen_rect.h = cg_core.game_height * scale;
+ cg_core.screen_rect.x = cg_core.screen_width/2 -
+ cg_core.screen_rect.w/2;
+ cg_core.screen_rect.y = 0;
+ }
+ // If they have the same aspect ratio.
+ else
+ {
+ cg_core.screen_rect.x = 0;
+ cg_core.screen_rect.y = 0;
+ cg_core.screen_rect.w = cg_core.screen_width;
+ cg_core.screen_rect.h = cg_core.screen_height;
+ }
+}
+
+static SDL_bool
+load_variables(void *obj, LoaderStack *ls)
+{
+ YAML::Node root = YAML::LoadFile(cg_core.config_file);
+
+ std::string game_name = root["name"].as<std::string>().c_str();
+ cg_core.game_name = new char[game_name.size() + 1];
+ strcpy(cg_core.game_name, game_name.c_str());
+
+ // Based on NES.
+ cg_core.game_width = root["game"]["width"].as<int>();
+ cg_core.game_height = root["game"]["height"].as<int>();
+ cg_core.screen_width = root["screen"]["width"].as<int>();
+ cg_core.screen_height = root["screen"]["height"].as<int>();
+
+ cg_core.fps = 30;
+ cg_core.max_frame_duration_ms = 1000 / cg_core.fps;
+
+ return SDL_TRUE;
+}
+
+static void
+unload_variables(void *obj, LoaderStack *ls)
+{
+ delete[] cg_core.game_name;
+}
+
+static SDL_bool
+load_sdl(void *obj, LoaderStack *ls)
+{
+ if(SDL_Init(SDL_INIT_EVERYTHING) < 0)
+ {
+ const char* base_error = "SDL could not initialize! SDL Error → ";
+ LoaderStack_set_error(ls, base_error, SDL_GetError());
+ return SDL_FALSE;
+ }
+
+ return SDL_TRUE;
+}
+
+static void
+unload_sdl(void *obj, LoaderStack *ls)
+{
+ SDL_Quit();
+}
+
+static SDL_bool
+load_sdl_image(void *obj, LoaderStack *ls)
+{
+ int flags = IMG_INIT_JPG|IMG_INIT_PNG|IMG_INIT_TIF;
+ if(!(IMG_Init(flags) & flags))
+ {
+ const char* base_error = "Could not initialize SDL image → ";
+ LoaderStack_set_error(ls, base_error, IMG_GetError());
+ return SDL_FALSE;
+ }
+
+ return SDL_TRUE;
+}
+
+static void
+unload_sdl_image(void *obj, LoaderStack *ls)
+{
+ IMG_Quit();
+}
+
+static SDL_bool
+load_sdl_mixer(void *obj, LoaderStack *ls)
+{
+ int flags = MIX_INIT_OGG|MIX_INIT_MOD;
+ int initted = Mix_Init(flags);
+ if(initted&flags != flags)
+ {
+ const char* base_error = "Could not initialize SDL mixer → ";
+ LoaderStack_set_error(ls, base_error, Mix_GetError());
+ return SDL_FALSE;
+ }
+
+ return SDL_TRUE;
+}
+
+static void
+unload_sdl_mixer(void *obj, LoaderStack *ls)
+{
+ while(Mix_Init(0)) Mix_Quit();
+}
+
+static SDL_bool
+load_sdl_open_audio(void *obj, LoaderStack *ls)
+{
+ if(Mix_OpenAudio(22050, MIX_DEFAULT_FORMAT, 2, 1024) == -1)
+ {
+ const char* base_error = "Could not open SDL mixer audio → ";
+ LoaderStack_set_error(ls, base_error, Mix_GetError());
+ return SDL_FALSE;
+ }
+
+ return SDL_TRUE;
+}
+
+static void
+unload_sdl_open_audio(void *obj, LoaderStack *ls)
+{
+ Mix_CloseAudio();
+}
+
+static SDL_bool
+load_sdl_ttf(void *obj, LoaderStack *ls)
+{
+ if(TTF_Init()==-1)
+ {
+ const char* base_error = "Could not initialize SDL ttf → ";
+ LoaderStack_set_error(ls, base_error, TTF_GetError());
+ return SDL_FALSE;
+ }
+
+ return SDL_TRUE;
+}
+
+static void
+unload_sdl_ttf(void *obj, LoaderStack *ls)
+{
+ TTF_Quit();
+}
+
+static SDL_bool
+load_window(void *obj, LoaderStack *ls)
+{
+ cg_core.window = NULL;
+ cg_core.window = SDL_CreateWindow(
+ cg_core.game_name, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
+ cg_core.screen_width, cg_core.screen_height, SDL_WINDOW_SHOWN);
+ if(cg_core.window == NULL)
+ {
+ const char* base_error = "Window could not be created! SDL_Error → ";
+ LoaderStack_set_error(ls, base_error, SDL_GetError());
+ return SDL_FALSE;
+ }
+
+ return SDL_TRUE;
+}
+
+static void
+unload_window(void *obj, LoaderStack *ls)
+{
+ SDL_DestroyWindow(cg_core.window);
+}
+
+static SDL_bool
+load_sdl_renderer(void *obj, LoaderStack *ls)
+{
+ cg_core.renderer = NULL;
+ cg_core.renderer = SDL_CreateRenderer(
+ cg_core.window, -1, SDL_RENDERER_ACCELERATED |
+ SDL_RENDERER_TARGETTEXTURE);
+ if(cg_core.renderer == NULL)
+ {
+ const char* base_error = "Could not create SDL renderer → ";
+ LoaderStack_set_error(ls, base_error, SDL_GetError());
+ return SDL_FALSE;
+ }
+
+ return SDL_TRUE;
+}
+
+static void
+unload_sdl_renderer(void *obj, LoaderStack *ls)
+{
+ SDL_DestroyRenderer(cg_core.renderer);
+}
+
+static SDL_bool
+load_pre_screen(void *obj, LoaderStack *ls)
+{
+ cg_core.pre_screen_buffer = SDL_CreateTexture(
+ cg_core.renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET,
+ cg_core.game_width, cg_core.game_height);
+
+ if(cg_core.pre_screen_buffer == NULL)
+ {
+ const char* base_error = "Could not create renderering buffer → ";
+ LoaderStack_set_error(ls, base_error, SDL_GetError());
+ return SDL_FALSE;
+ }
+
+ cg_Engine_calculate_full_scale();
+
+ SDL_SetRenderTarget(cg_core.renderer, cg_core.pre_screen_buffer);
+
+ return SDL_TRUE;
+}
+
+static void
+unload_pre_screen(void *obj, LoaderStack *ls)
+{
+ SDL_DestroyTexture(cg_core.pre_screen_buffer);
+}
+
+void
+cg_Core_init(LoaderStack *loader)
+{
+ LoaderStack_constructor(loader, NULL);
+
+ LoaderStack_add(loader, &load_variables, &unload_variables);
+ LoaderStack_add(loader, &load_sdl, &unload_sdl);
+ LoaderStack_add(loader, &load_sdl_image, &unload_sdl_image);
+ LoaderStack_add(loader, &load_sdl_mixer, &unload_sdl_mixer);
+ LoaderStack_add(loader, &load_sdl_open_audio, &unload_sdl_open_audio);
+ LoaderStack_add(loader, &load_sdl_ttf, &unload_sdl_ttf);
+ LoaderStack_add(loader, &load_window, &unload_window);
+ LoaderStack_add(loader, &load_sdl_renderer, &unload_sdl_renderer);
+ LoaderStack_add(loader, &load_pre_screen, &unload_pre_screen);
+}
+
+SDL_bool
+cg_Core_load(LoaderStack *loader)
+{
+ if(!LoaderStack_load(loader)) return SDL_FALSE;
+
+ return SDL_TRUE;
+}
+
+void
+cg_Core_unload(LoaderStack *loader)
+{
+ LoaderStack_destructor(loader);
+}
diff --git a/src/core.h b/src/core.h
new file mode 100644
index 0000000..e5d651a
--- /dev/null
+++ b/src/core.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CANDY_GEAR_CORE_H
+#define CANDY_GEAR_CORE_H 1
+
+#include <mruby.h>
+#include <mruby/class.h>
+#include <mruby/compile.h>
+#include <mruby/data.h>
+#include <mruby/dump.h>
+#include <mruby/variable.h>
+
+#include <SDL2/SDL.h>
+#include <SDL2/SDL_image.h>
+#include <SDL2/SDL_mixer.h>
+#include <SDL2/SDL_ttf.h>
+
+#include "loader.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * The Core class stores all global states that the engine needs to work.
+ * Global variables are not evil if you use them carefully.
+ */
+typedef struct
+{
+ const char *config_file;
+
+ /// Text displayed in the game window.
+ char *game_name;
+
+ /**
+ * @{
+ * This is the amount of pixel the games use to render a buffer. The image in
+ * this buffer is then rendered to the screen.
+ */
+ int game_width, game_height;
+ /// @}
+
+ /**
+ * @{
+ * This is the ammount of pixel that the games uses when rendering to the
+ * screen.
+ */
+ int screen_width, screen_height;
+ /// @}
+
+ Uint32 fps;
+ Uint32 max_frame_duration_ms;
+
+ SDL_Window *window;
+ SDL_Renderer *renderer;
+
+ /// All rendering goes here before they are moved to the screen.
+ SDL_Texture *pre_screen_buffer;
+ SDL_Rect screen_rect;
+
+ SDL_bool quit_game;
+} cg_sCore;
+
+extern cg_sCore cg_core;
+
+/// \memberof cg_sCore @{
+
+void
+cg_Core_init(LoaderStack *loader);
+
+SDL_bool
+cg_Core_load(LoaderStack *loader);
+
+void
+cg_Core_unload(LoaderStack *loader);
+
+/// @}
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* CANDY_GEAR_CORE_H */
diff --git a/src/font.c b/src/font.c
new file mode 100644
index 0000000..a333a30
--- /dev/null
+++ b/src/font.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "font.h"
+#include <stdio.h>
+
+void
+cg_free_font(mrb_state *mrb, void* obj)
+{
+ struct cg_font *ptr = obj;
+
+ TTF_CloseFont(ptr->data);
+ mrb_free(mrb, ptr);
+}
+
+const struct mrb_data_type cg_font_type = {"CG_Font", cg_free_font };
+
+static mrb_value
+cg_cFont_initialize(mrb_state *mrb, mrb_value self)
+{
+ mrb_int font_size;
+ char *file_path;
+ struct cg_font *ptr;
+
+ mrb_get_args(mrb, "zi", &file_path, &font_size);
+ ptr = (struct cg_font *)DATA_PTR(self);
+ if(ptr) mrb_free(mrb, ptr);
+ ptr = (struct cg_font *)mrb_malloc(mrb, sizeof(struct cg_font));
+
+ ptr->data = TTF_OpenFont(file_path, font_size);
+ if(!ptr->data) mrb_raise(mrb, E_ARGUMENT_ERROR, TTF_GetError());
+
+ mrb_data_init(self, ptr, &cg_font_type);
+ return self;
+}
+
+void
+cg_font_init(mrb_state *mrb)
+{
+ struct RClass *cg_m, *cg_cFont;
+
+ cg_m = mrb_module_get(mrb, "CandyGear");
+ cg_cFont = mrb_define_class_under(mrb, cg_m, "Font", mrb->object_class);
+ MRB_SET_INSTANCE_TT(cg_cFont, MRB_TT_DATA);
+ mrb_define_method(
+ mrb, cg_cFont, "initialize", cg_cFont_initialize, MRB_ARGS_REQ(2));
+}
diff --git a/src/font.h b/src/font.h
new file mode 100644
index 0000000..55e4d16
--- /dev/null
+++ b/src/font.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CANDY_GEAR_FONT_H
+#define CANDY_GEAR_FONT_H 1
+
+#include "core.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct cg_font
+{
+ TTF_Font *data;
+};
+
+extern const struct mrb_data_type cg_font_type;
+
+void
+cg_font_init(mrb_state *mrb);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* CANDY_GEAR_FONT_H */
diff --git a/src/graphic.c b/src/graphic.c
new file mode 100644
index 0000000..b58c08c
--- /dev/null
+++ b/src/graphic.c
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "graphic.h"
+
+#include "color.h"
+
+static mrb_value
+cg_mGraphic_set_color(mrb_state *mrb, mrb_value self)
+{
+ struct cg_color *color;
+ mrb_get_args(mrb, "d", &color, &cg_color_type);
+
+ SDL_SetRenderDrawColor(
+ cg_core.renderer, color->data.r, color->data.g, color->data.b,
+ color->data.a);
+
+ return self;
+}
+
+static mrb_value
+cg_mGraphic_draw_point(mrb_state *mrb, mrb_value self)
+{
+ mrb_int x, y;
+ mrb_get_args(mrb, "ii", &x, &y);
+ SDL_RenderDrawPoint(cg_core.renderer, x, y);
+
+ return self;
+}
+
+static mrb_value
+cg_mGraphic_draw_line(mrb_state *mrb, mrb_value self)
+{
+ mrb_int x1, y1, x2, y2;
+ mrb_get_args(mrb, "iiii", &x1, &y1, &x2, &y2);
+ SDL_RenderDrawLine(cg_core.renderer, x1, y1, x2, y2);
+
+ return self;
+}
+
+void
+cg_graphic_init(mrb_state *mrb)
+{
+ struct RClass *cg_m, *cg_mGraphic;
+
+ cg_m = mrb_module_get(mrb, "CandyGear");
+ cg_mGraphic = mrb_define_module_under(mrb, cg_m, "Graphic");
+ mrb_define_class_method(
+ mrb, cg_mGraphic, "set_color", cg_mGraphic_set_color, MRB_ARGS_REQ(1));
+ mrb_define_class_method(
+ mrb, cg_mGraphic, "draw_point", cg_mGraphic_draw_point, MRB_ARGS_REQ(2));
+ mrb_define_class_method(
+ mrb, cg_mGraphic, "draw_line", cg_mGraphic_draw_line, MRB_ARGS_REQ(4));
+}
diff --git a/src/graphic.h b/src/graphic.h
new file mode 100644
index 0000000..3fe9b1e
--- /dev/null
+++ b/src/graphic.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CANDY_GEAR_GRAPHIC_H
+#define CANDY_GEAR_GRAPHIC_H 1
+
+#include "core.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void
+cg_graphic_init(mrb_state *mrb);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* CANDY_GEAR_GRAPHIC_H */
diff --git a/src/key.c b/src/key.c
new file mode 100644
index 0000000..01fc42a
--- /dev/null
+++ b/src/key.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "key.h"
+
+void
+cg_key_init(mrb_state *mrb)
+{
+ struct RClass *cg_m, *cg_mKey;
+
+ cg_m = mrb_module_get(mrb, "CandyGear");
+ cg_mKey = mrb_define_module_under(mrb, cg_m, "Key");
+
+ mrb_define_const(mrb, cg_mKey, "A", mrb_int_value(mrb, SDLK_a));
+ mrb_define_const(mrb, cg_mKey, "B", mrb_int_value(mrb, SDLK_b));
+ mrb_define_const(mrb, cg_mKey, "C", mrb_int_value(mrb, SDLK_c));
+ mrb_define_const(mrb, cg_mKey, "D", mrb_int_value(mrb, SDLK_d));
+ mrb_define_const(mrb, cg_mKey, "E", mrb_int_value(mrb, SDLK_e));
+ mrb_define_const(mrb, cg_mKey, "F", mrb_int_value(mrb, SDLK_f));
+ mrb_define_const(mrb, cg_mKey, "G", mrb_int_value(mrb, SDLK_g));
+ mrb_define_const(mrb, cg_mKey, "H", mrb_int_value(mrb, SDLK_h));
+ mrb_define_const(mrb, cg_mKey, "I", mrb_int_value(mrb, SDLK_i));
+ mrb_define_const(mrb, cg_mKey, "J", mrb_int_value(mrb, SDLK_j));
+ mrb_define_const(mrb, cg_mKey, "K", mrb_int_value(mrb, SDLK_k));
+ mrb_define_const(mrb, cg_mKey, "L", mrb_int_value(mrb, SDLK_l));
+ mrb_define_const(mrb, cg_mKey, "M", mrb_int_value(mrb, SDLK_m));
+ mrb_define_const(mrb, cg_mKey, "N", mrb_int_value(mrb, SDLK_n));
+ mrb_define_const(mrb, cg_mKey, "O", mrb_int_value(mrb, SDLK_o));
+ mrb_define_const(mrb, cg_mKey, "P", mrb_int_value(mrb, SDLK_p));
+ mrb_define_const(mrb, cg_mKey, "Q", mrb_int_value(mrb, SDLK_q));
+ mrb_define_const(mrb, cg_mKey, "R", mrb_int_value(mrb, SDLK_r));
+ mrb_define_const(mrb, cg_mKey, "S", mrb_int_value(mrb, SDLK_s));
+ mrb_define_const(mrb, cg_mKey, "T", mrb_int_value(mrb, SDLK_t));
+ mrb_define_const(mrb, cg_mKey, "U", mrb_int_value(mrb, SDLK_u));
+ mrb_define_const(mrb, cg_mKey, "V", mrb_int_value(mrb, SDLK_v));
+ mrb_define_const(mrb, cg_mKey, "W", mrb_int_value(mrb, SDLK_w));
+ mrb_define_const(mrb, cg_mKey, "X", mrb_int_value(mrb, SDLK_x));
+ mrb_define_const(mrb, cg_mKey, "Y", mrb_int_value(mrb, SDLK_y));
+ mrb_define_const(mrb, cg_mKey, "Z", mrb_int_value(mrb, SDLK_x));
+
+ mrb_define_const(mrb, cg_mKey, "UP", mrb_int_value(mrb, SDLK_UP));
+ mrb_define_const(mrb, cg_mKey, "DOWN", mrb_int_value(mrb, SDLK_DOWN));
+ mrb_define_const(mrb, cg_mKey, "LEFT", mrb_int_value(mrb, SDLK_LEFT));
+ mrb_define_const(mrb, cg_mKey, "RIGHT", mrb_int_value(mrb, SDLK_RIGHT));
+
+ mrb_define_const(
+ mrb, cg_mKey, "BACKSPACE", mrb_int_value(mrb, SDLK_BACKSPACE));
+ mrb_define_const(mrb, cg_mKey, "TAB", mrb_int_value(mrb, SDLK_TAB));
+ mrb_define_const(
+ mrb, cg_mKey, "LEFT_SHIFT", mrb_int_value(mrb, SDLK_LSHIFT));
+ mrb_define_const(
+ mrb, cg_mKey, "RIGHT_SHIFT", mrb_int_value(mrb, SDLK_RSHIFT));
+ mrb_define_const(mrb, cg_mKey, "SPACE", mrb_int_value(mrb, SDLK_SPACE));
+ mrb_define_const(mrb, cg_mKey, "LEFT_ALT", mrb_int_value(mrb, SDLK_LALT));
+ mrb_define_const(mrb, cg_mKey, "RIGHT_ALT", mrb_int_value(mrb, SDLK_RALT));
+ mrb_define_const(mrb, cg_mKey, "LEFT_CTRL", mrb_int_value(mrb, SDLK_LCTRL));
+ mrb_define_const(mrb, cg_mKey, "RIGHT_CTRL", mrb_int_value(mrb, SDLK_RCTRL));
+}
diff --git a/src/key.h b/src/key.h
new file mode 100644
index 0000000..5544518
--- /dev/null
+++ b/src/key.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CANDY_GEAR_KEY_H
+#define CANDY_GEAR_KEY_H 1
+
+#include "core.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void
+cg_key_init(mrb_state *mrb);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* CANDY_GEAR_KEY_H */
diff --git a/src/loader.c b/src/loader.c
new file mode 100644
index 0000000..c24d47e
--- /dev/null
+++ b/src/loader.c
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+
+#include "loader.h"
+
+void
+LoaderStack_constructor(LoaderStack *self, void *obj)
+{
+ self->_last_loaded = 0;
+
+ self->_loader_capacity = 10; // Totally arbitrary value.
+ self->_loader_size = 0;
+
+ self->_obj = obj;
+
+ self->error_message = NULL;
+
+ self->_loaders = malloc(sizeof(Loader) * self->_loader_capacity);
+}
+
+void
+LoaderStack_destructor(LoaderStack *self)
+{
+ if(self->error_message != NULL)
+ free(self->error_message);
+
+ if(LoaderStack_is_loaded(self)) LoaderStack_unload(self);
+ free(self->_loaders);
+}
+
+static void
+LoaderStack_partial_unload(LoaderStack *self, int32_t step)
+{
+ // Already unloaded, nothing to do.
+ if(self->_last_loaded <= step) return;
+
+ for(; self->_last_loaded >= step; self->_last_loaded--)
+ if(self->_loaders[self->_last_loaded].unload != NULL)
+ self->_loaders[self->_last_loaded].unload(self->_obj, self);
+
+ // This number will be one before the last loaded after the unloading loop
+ // is over.
+ self->_last_loaded++;
+}
+
+SDL_bool
+LoaderStack_partial_load(LoaderStack *self, int32_t step)
+{
+ SDL_bool error = SDL_FALSE;
+
+ // Already loaded, nothing to do.
+ if(self->_last_loaded >= step) return SDL_TRUE;
+
+ for(;self->_last_loaded < step; self->_last_loaded++)
+ {
+ error = !self->_loaders[self->_last_loaded].load(self->_obj, self);
+ if(error) break;
+ }
+
+ // Self number will be one after the last loeaded after the loading loop is
+ // over.
+ self->_last_loaded--;
+
+ if(error) LoaderStack_partial_unload(self, 0);
+
+ return !error;
+}
+
+SDL_bool
+LoaderStack_is_loaded(LoaderStack *self)
+{
+ return self->_last_loaded > 0;
+}
+
+void
+LoaderStack_add(
+ LoaderStack *self,
+ SDL_bool (*load)(void *obj, LoaderStack *ls),
+ void (*unload)(void *obj, LoaderStack *ls))
+{
+ Loader *l;
+ uint32_t next_loader = self->_loader_size;
+
+ // Expand if is full.
+ if(self->_loader_size == self->_loader_capacity)
+ {
+ self->_loader_capacity += 5; // Totally arbitrary value.
+ self->_loaders = realloc(self->_loaders,
+ sizeof(Loader) * self->_loader_capacity);
+ }
+
+ l = &(self->_loaders[next_loader]);
+ l->load = load;
+ l->unload = unload;
+
+ self->_loader_size++;
+}
+
+void
+LoaderStack_set_error(
+ LoaderStack *self, const char* base_error, const char* additional_error)
+{
+ if(self->error_message != NULL) free(self->error_message);
+
+ if(additional_error != NULL)
+ {
+ self->error_message =
+ malloc(strlen(base_error) + strlen(additional_error) + 1);
+
+ strcpy(self->error_message, base_error);
+ strcat(self->error_message, additional_error);
+ }
+ else
+ {
+ self->error_message = malloc(strlen(base_error));
+
+ strcpy(self->error_message, base_error);
+ }
+}
+
+SDL_bool
+LoaderStack_load(LoaderStack *self)
+{
+ if(LoaderStack_is_loaded(self)) return SDL_TRUE;
+
+ if(LoaderStack_partial_load(self, self->_loader_size))
+ return SDL_TRUE;
+ else
+ return SDL_FALSE;
+}
+
+SDL_bool
+LoaderStack_reload(LoaderStack *self)
+{
+ if(!LoaderStack_is_loaded(self)) return SDL_FALSE;
+
+ LoaderStack_partial_unload(self, self->_loader_size);
+ return LoaderStack_partial_load(self, self->_loader_size);
+}
+
+void
+LoaderStack_unload(LoaderStack *self)
+{
+ if(!LoaderStack_is_loaded(self)) return;
+
+ LoaderStack_partial_unload(self, 0);
+}
diff --git a/src/loader.h b/src/loader.h
new file mode 100644
index 0000000..93ec42b
--- /dev/null
+++ b/src/loader.h
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CANDY_GEAR_LOADER_STACK_H
+#define CANDY_GEAR_LOADER_STACK_H 1
+
+#include <SDL2/SDL.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct LoaderStack_s;
+
+/**
+ * Stores a reversible action.
+ */
+typedef struct
+{
+ SDL_bool (*load)(void *obj, struct LoaderStack_s *ls);
+ void (*unload)(void *obj, struct LoaderStack_s *ls);
+} Loader;
+
+/**
+ * Stores a sequence of functions that must be executed and rolled back in
+ * order.
+ *
+ * For example, if the variable _loaders contain A→B→C→D→E, it will load A,
+ * then B, then C, etc. If D fails, it unloads C, then B, then A.
+ */
+struct LoaderStack_s
+{
+ void *_obj;
+
+ int32_t _last_loaded;
+
+ uint32_t _loader_capacity;
+ uint32_t _loader_size;
+ Loader *_loaders;
+
+ char* error_message;
+};
+
+typedef struct LoaderStack_s LoaderStack;
+
+/// \memberof LoaderStack_s @{
+
+/**
+ * Must be called to initialize the variables.
+ *
+ * @param[self] a pointer to the LoaderStack_s to be initalized.
+ * @param[obj] a pointer to an struct that will be send to every function
+ * inside the _loaders.
+ */
+void
+LoaderStack_constructor(LoaderStack *self, void *obj);
+
+/**
+ * Must be called to finalize the variables and cleanup memory.
+ *
+ * @param[self] a pointer to the LoaderStack_s to be finalized.
+ */
+void
+LoaderStack_destructor(LoaderStack *self);
+
+/**
+ * Adds a reversible action to the loader. The first action added is the first
+ * to de executed the last rolled back, and so on. Those functions are stored
+ * inside _loaders.
+ *
+ * @param[self] a pointer to the LoaderStack_s hat stores the action.
+ * @param[load] the do action. The function pointed by this variable must
+ * return SDL_TRUE on success and SDL_FALSE on fail.
+ * @param[unload] the undo/rollback action, if the action do not need a
+ * rollback, this pointer can be set to null.
+ */
+void
+LoaderStack_add(
+ LoaderStack *self,
+ SDL_bool (*load)(void *obj, LoaderStack *ls),
+ void (*unload)(void *obj, LoaderStack *ls));
+
+/**
+ * Can be used to set error before returning SDL_FALSE from a function used by
+ * _loaders
+ *
+ * @param[self] a pointer to the LoaderStack_s where the error happened.
+ * @param[base_error] the error message.
+ * @param[additional_error] if you need to concatenate two error messages, use
+ * this variable to a second message. Otherwise set to NULL.
+ */
+void
+LoaderStack_set_error(
+ LoaderStack *self, const char* base_error, const char* additional_error);
+
+/**
+ * @return SDL_TRUE if you successfuly executed LoaderStack_load or
+ * LoaderStack_reload in the LoaderStack. SDL_FALSE after
+ * LoaderStack_constructor or LoaderStack_unload is executed.
+ */
+SDL_bool
+LoaderStack_is_loaded(LoaderStack *self);
+
+/**
+ * Execute some of the load functions in the _loaders. If one of them fails,
+ * roll back everything inside _loaders. The LoaderStack knows which functions
+ * it already executed and will not rerun them unless they are rolled back
+ * first.
+ *
+ * @param[step] the number of functions to be executed.
+ * @return SDL_TRUE on success and SDL_FALSE on fail.
+ */
+SDL_bool
+LoaderStack_partial_load(LoaderStack *self, int32_t step);
+
+/**
+ * Execute all of the load functions in the _loaders. If one of them fails,
+ * roll back everything inside _loaders.
+ * @return SDL_TRUE on success and SDL_FALSE on fail.
+ */
+SDL_bool
+LoaderStack_load(LoaderStack *self);
+
+/**
+ * Roll back all loaded function inside loaders, if there are any. Then execute
+ * all of the load functions in the _loaders. If one of them fails, roll back
+ * everything inside _loaders.
+ *
+ * @return SDL_TRUE on success and SDL_FALSE on fail.
+ */
+SDL_bool
+LoaderStack_reload(LoaderStack *self);
+
+/**
+ * Roll back all loaded function inside loaders, if there are any.
+ */
+void
+LoaderStack_unload(LoaderStack *self);
+
+/// @}
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* CANDY_GEAR_LOADER_STACK_H */
diff --git a/src/log.c b/src/log.c
new file mode 100644
index 0000000..92862ae
--- /dev/null
+++ b/src/log.c
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "log.h"
+
+#include <stdio.h>
+
+static mrb_value
+cg_mLog_info(mrb_state *mrb, mrb_value self)
+{
+ const char *message;
+ mrb_get_args(mrb, "z", &message);
+ printf("%s\n", message);
+
+ return self;
+}
+
+void
+cg_log_init(mrb_state *mrb)
+{
+ struct RClass *cg_m, *cg_mLog;
+ cg_m = mrb_module_get(mrb, "CandyGear");
+ cg_mLog = mrb_define_module_under(mrb, cg_m, "Log");
+ mrb_define_class_method(mrb, cg_mLog, "info", cg_mLog_info, MRB_ARGS_REQ(1));
+}
diff --git a/src/log.h b/src/log.h
new file mode 100644
index 0000000..0196908
--- /dev/null
+++ b/src/log.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CANDY_GEAR_LOG_H
+#define CANDY_GEAR_LOG_H 1
+
+#include "core.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void
+cg_log_init(mrb_state *mrb);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* CANDY_GEAR_LOG_H */
diff --git a/src/main.cpp b/src/main.cpp
new file mode 100644
index 0000000..8ebe77c
--- /dev/null
+++ b/src/main.cpp
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "candy_gear.h"
+#include "color.h"
+#include "core.h"
+#include "font.h"
+#include "graphic.h"
+#include "key.h"
+#include "log.h"
+#include "palette.h"
+#include "point.h"
+#include "rect.h"
+#include "sound.h"
+#include "sprite.h"
+#include "texture.h"
+
+cg_sCore cg_core;
+
+void handle_error(mrb_state *mrb)
+{
+ mrb_print_error(mrb);
+ cg_core.quit_game = SDL_TRUE;
+}
+
+int main(int argc, char *argv[])
+{
+ SDL_Event event;
+ Uint32 frame_start;
+
+ mrb_state *mrb = mrb_open();
+ mrb_value main_obj;
+ mrb_sym sym_init, sym_key_down, sym_key_up, sym_quit, sym_tick;
+ FILE *fp;
+
+ LoaderStack core_loader;
+
+ cg_core.config_file = argv[1];
+
+ cg_Core_init(&core_loader);
+ if(!cg_Core_load(&core_loader)) return 1;
+
+ if (!mrb) { /* handle error */ }
+ mrb_define_module(mrb, "CandyGear");
+ cg_candy_gear_init(mrb);
+ cg_color_init(mrb);
+ cg_font_init(mrb);
+ cg_graphic_init(mrb);
+ cg_key_init(mrb);
+ cg_log_init(mrb);
+ cg_palette_init(mrb);
+ cg_point_init(mrb);
+ cg_rect_init(mrb);
+ cg_sound_init(mrb);
+ cg_sprite_init(mrb);
+ cg_texture_init(mrb);
+
+ main_obj = mrb_obj_iv_inspect(mrb, mrb->top_self);
+ sym_init = mrb_intern_cstr(mrb, "init");
+ sym_key_down = mrb_intern_cstr(mrb, "key_down");
+ sym_key_up = mrb_intern_cstr(mrb, "key_up");
+ sym_quit = mrb_intern_cstr(mrb, "quit");
+ sym_tick = mrb_intern_cstr(mrb, "tick");
+
+ fp = fopen(argv[2], "r");
+ mrb_load_irep_file(mrb, fp);
+ fclose(fp);
+ if (mrb->exc) handle_error(mrb);
+
+ mrb_funcall_id(mrb, main_obj, sym_init, 0);
+ if (mrb->exc) handle_error(mrb);
+
+ frame_start = SDL_GetTicks();
+
+ // Game main loop.
+ while(!cg_core.quit_game)
+ {
+ // Get input.
+ while(SDL_PollEvent(&event) != 0)
+ {
+ switch(event.type)
+ {
+ case SDL_KEYDOWN:
+ mrb_funcall_id(
+ mrb, main_obj, sym_key_down, 1,
+ mrb_int_value(mrb, event.key.keysym.sym));
+ break;
+ case SDL_KEYUP:
+ mrb_funcall_id(
+ mrb, main_obj, sym_key_up, 1,
+ mrb_int_value(mrb, event.key.keysym.sym));
+ break;
+ case SDL_MOUSEMOTION:
+ break;
+ case SDL_MOUSEBUTTONDOWN:
+ break;
+ case SDL_MOUSEBUTTONUP:
+ break;
+ case SDL_QUIT:
+ mrb_funcall_id(mrb, main_obj, sym_quit, 0);
+ break;
+ }
+ }
+
+ // Clear buffer.
+ SDL_SetRenderDrawColor(cg_core.renderer, 0x00, 0x00, 0x00, 0xff);
+ SDL_RenderClear(cg_core.renderer);
+
+ mrb_funcall_id(mrb, main_obj, sym_tick, 0);
+ if (mrb->exc) handle_error(mrb);
+
+ // Copy buffer to the screen and display it.
+ SDL_SetRenderTarget(cg_core.renderer, NULL);
+ SDL_RenderCopy(
+ cg_core.renderer, cg_core.pre_screen_buffer, NULL, &cg_core.screen_rect);
+ SDL_RenderPresent(cg_core.renderer);
+ SDL_SetRenderTarget(cg_core.renderer, cg_core.pre_screen_buffer);
+
+ // Timer
+ {
+ Uint32 frame_stop, frame_duration;
+
+ frame_stop = SDL_GetTicks();
+ frame_duration = frame_stop - frame_start;
+
+ // If frame take less time than maximum allowed.
+ if(cg_core.max_frame_duration_ms > frame_duration)
+ SDL_Delay(cg_core.max_frame_duration_ms - frame_duration);
+
+ frame_start = frame_stop;
+ }
+ }
+
+ mrb_close(mrb);
+ cg_Core_unload(&core_loader);
+
+ return 0;
+}
diff --git a/src/palette.c b/src/palette.c
new file mode 100644
index 0000000..9f98dc6
--- /dev/null
+++ b/src/palette.c
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "palette.h"
+#include "palette_implementation.h"
+
+void
+cg_free_palette(mrb_state *mrb, void* obj)
+{
+ struct cg_palette *ptr = obj;
+
+ mrb_free(mrb, ptr);
+}
+
+const struct mrb_data_type cg_palette_type = {
+ "CG_Palette", cg_free_palette };
+
+static mrb_value
+cg_cPalette_initialize(mrb_state *mrb, mrb_value self)
+{
+ const char *file_path;
+ struct cg_palette *ptr;
+
+ mrb_get_args(mrb, "z", &file_path);
+ ptr = (struct cg_palette *)DATA_PTR(self);
+ if(ptr) mrb_free(mrb, ptr);
+ ptr = (struct cg_palette *)mrb_malloc(mrb, sizeof(struct cg_palette));
+
+ cg_pallet_load_yaml(ptr, file_path);
+
+ mrb_data_init(self, ptr, &cg_palette_type);
+ return self;
+}
+
+void
+cg_palette_init(mrb_state *mrb)
+{
+ struct RClass *cg_m, *cg_cPalette;
+
+ cg_m = mrb_module_get(mrb, "CandyGear");
+ cg_cPalette = mrb_define_class_under(mrb, cg_m, "Palette", mrb->object_class);
+ MRB_SET_INSTANCE_TT(cg_cPalette, MRB_TT_DATA);
+ mrb_define_method(
+ mrb, cg_cPalette, "initialize", cg_cPalette_initialize, MRB_ARGS_REQ(1));
+}
diff --git a/src/palette.h b/src/palette.h
new file mode 100644
index 0000000..b9e52c2
--- /dev/null
+++ b/src/palette.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CANDY_GEAR_PALETTE_H
+#define CANDY_GEAR_PALETTE_H 1
+
+#include "core.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct cg_palette
+{
+ SDL_Color colors[256];
+};
+
+extern const struct mrb_data_type cg_palette_type;
+
+void
+cg_palette_init(mrb_state *mrb);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* CANDY_GEAR_PALETTE_H */
diff --git a/src/palette_implementation.cpp b/src/palette_implementation.cpp
new file mode 100644
index 0000000..7f85c6a
--- /dev/null
+++ b/src/palette_implementation.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "palette_implementation.h"
+
+#include <string>
+
+#include <yaml-cpp/yaml.h>
+
+void cg_pallet_load_yaml(struct cg_palette *palette, const char *file_path)
+{
+ YAML::Node root = YAML::LoadFile(file_path);
+ for(std::size_t i = 0; i < root.size(); i++)
+ {
+ std::string color = root[i].as<std::string>();
+
+ palette->colors[i].r = std::stoi(color.substr(0, 2), nullptr, 16);
+ palette->colors[i].g = std::stoi(color.substr(2, 2), nullptr, 16);
+ palette->colors[i].b = std::stoi(color.substr(4, 2), nullptr, 16);
+ palette->colors[i].a = std::stoi(color.substr(6, 2), nullptr, 16);
+ }
+}
diff --git a/src/palette_implementation.h b/src/palette_implementation.h
new file mode 100644
index 0000000..50c9ef3
--- /dev/null
+++ b/src/palette_implementation.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CANDY_GEAR_PALETTE_IMPLEMENTATION_H
+#define CANDY_GEAR_PALETTE_IMPLEMENTATION_H 1
+
+#include "palette.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void
+cg_pallet_load_yaml(struct cg_palette *palette, const char *file_path);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* CANDY_GEAR_PALETTE_IMPLEMENTATION_H */
diff --git a/src/pgm_image.cpp b/src/pgm_image.cpp
new file mode 100644
index 0000000..a88455c
--- /dev/null
+++ b/src/pgm_image.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "pgm_image.h"
+
+#include <cstring>
+#include <charconv>
+#include <fstream>
+#include <string>
+
+SDL_bool PGMImage_constructor(PGMImage *self, const char *file_path)
+{
+ self->data = nullptr;
+ int wh_pos;
+ std::string line;
+ std::ifstream file(file_path);
+
+ if(!file)
+ {
+ self->error = "Failed to open PGM file";
+ return SDL_FALSE;
+ }
+
+ // Read file magic.
+ std::getline(file, line);
+ if(line != "P5")
+ {
+ self->error = "PGM file contains a invalid magic";
+ return SDL_FALSE;
+ }
+
+ // Read file comment.
+ std::getline(file, line);
+
+ // Read file width and height.
+ std::getline(file, line);
+ wh_pos = line.find(" ");
+ std::from_chars(line.data(), line.data() + wh_pos, self->width);
+ std::from_chars(
+ line.data() + wh_pos + 1, line.data() + line.size(), self->height);
+
+ // Read file maximum value.
+ std::getline(file, line);
+ std::from_chars(line.data(), line.data() + line.size(), self->max_value);
+
+ // Read file values.
+ self->data_size = self->width * self->height;
+ self->data = new char[self->data_size];
+ file.read(self->data, self->data_size);
+
+ return SDL_TRUE;
+}
+
+void PGMImage_destructor(PGMImage *self)
+{
+ if(self->data != nullptr) delete[] self->data;
+}
diff --git a/src/pgm_image.h b/src/pgm_image.h
new file mode 100644
index 0000000..5a271d0
--- /dev/null
+++ b/src/pgm_image.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CANDY_GEAR_PGM_IMAGE_H
+#define CANDY_GEAR_PGM_IMAGE_H 1
+
+#include <SDL2/SDL.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct PGMImage_s
+{
+ int width, height, max_value, data_size;
+ char *data;
+ const char *error;
+};
+
+typedef struct PGMImage_s PGMImage;
+
+SDL_bool PGMImage_constructor(PGMImage *self, const char *file_path);
+void PGMImage_destructor(PGMImage *self);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* CANDY_GEAR_PGM_IMAGE_H */
diff --git a/src/point.c b/src/point.c
new file mode 100644
index 0000000..4d11480
--- /dev/null
+++ b/src/point.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "point.h"
+
+void
+cg_free_point(mrb_state *mrb, void* obj)
+{
+ struct cg_point *ptr = obj;
+
+ mrb_free(mrb, ptr);
+}
+
+const struct mrb_data_type cg_point_type = {
+ "CG_Point", cg_free_point };
+
+static mrb_value
+cg_cPoint_initialize(mrb_state *mrb, mrb_value self)
+{
+ mrb_int x, y;
+ struct cg_point *ptr;
+
+ mrb_get_args(mrb, "ii", &x, &y);
+ ptr = (struct cg_point *)DATA_PTR(self);
+ if(ptr) mrb_free(mrb, ptr);
+ ptr = (struct cg_point *)mrb_malloc(mrb, sizeof(struct cg_point));
+
+ ptr->data.x = x;
+ ptr->data.y = y;
+
+ mrb_data_init(self, ptr, &cg_point_type);
+ return self;
+}
+
+static mrb_value
+cg_cPoint_get_x(mrb_state *mrb, mrb_value self)
+{
+ struct cg_point *ptr;
+
+ ptr = (struct cg_point *)DATA_PTR(self);
+
+ return mrb_int_value(mrb, ptr->data.x);
+}
+
+static mrb_value
+cg_cPoint_set_x(mrb_state *mrb, mrb_value self)
+{
+ mrb_int x;
+ struct cg_point *ptr;
+
+ mrb_get_args(mrb, "i", &x);
+ ptr = (struct cg_point *)DATA_PTR(self);
+
+ ptr->data.x = x;
+
+ return mrb_int_value(mrb, ptr->data.x);
+}
+
+static mrb_value
+cg_cPoint_get_y(mrb_state *mrb, mrb_value self)
+{
+ struct cg_point *ptr;
+
+ ptr = (struct cg_point *)DATA_PTR(self);
+
+ return mrb_int_value(mrb, ptr->data.y);
+}
+
+static mrb_value
+cg_cPoint_set_y(mrb_state *mrb, mrb_value self)
+{
+ mrb_int y;
+ struct cg_point *ptr;
+
+ mrb_get_args(mrb, "i", &y);
+ ptr = (struct cg_point *)DATA_PTR(self);
+
+ ptr->data.y = y;
+
+ return mrb_int_value(mrb, ptr->data.y);
+}
+
+static mrb_value
+cg_cPoint_draw(mrb_state *mrb, mrb_value self)
+{
+ struct cg_point *ptr;
+ ptr = (struct cg_point *)DATA_PTR(self);
+ SDL_RenderDrawPoint(cg_core.renderer, ptr->data.x, ptr->data.y);
+
+ return self;
+}
+
+void
+cg_point_init(mrb_state *mrb)
+{
+ struct RClass *cg_m, *cg_cPoint;
+
+ cg_m = mrb_module_get(mrb, "CandyGear");
+ cg_cPoint = mrb_define_class_under(
+ mrb, cg_m, "Point", mrb->object_class);
+ MRB_SET_INSTANCE_TT(cg_cPoint, MRB_TT_DATA);
+ mrb_define_method(
+ mrb, cg_cPoint, "initialize", cg_cPoint_initialize, MRB_ARGS_REQ(2));
+ mrb_define_method(mrb, cg_cPoint, "x", cg_cPoint_get_x, MRB_ARGS_NONE());
+ mrb_define_method(mrb, cg_cPoint, "x=", cg_cPoint_set_x, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, cg_cPoint, "y", cg_cPoint_get_y, MRB_ARGS_NONE());
+ mrb_define_method(mrb, cg_cPoint, "y=", cg_cPoint_set_y, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, cg_cPoint, "draw", cg_cPoint_draw, MRB_ARGS_NONE());
+}
diff --git a/src/point.h b/src/point.h
new file mode 100644
index 0000000..7507d62
--- /dev/null
+++ b/src/point.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CANDY_GEAR_POINT_H
+#define CANDY_GEAR_POINT_H 1
+
+#include "core.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct cg_point
+{
+ SDL_Point data;
+};
+
+extern const struct mrb_data_type cg_point_type;
+
+void
+cg_point_init(mrb_state *mrb);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* CANDY_GEAR_POINT_H */
diff --git a/src/rect.c b/src/rect.c
new file mode 100644
index 0000000..206ab45
--- /dev/null
+++ b/src/rect.c
@@ -0,0 +1,209 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "rect.h"
+
+void
+cg_free_rect(mrb_state *mrb, void* obj)
+{
+ struct cg_rect *ptr = obj;
+
+ mrb_free(mrb, ptr);
+}
+
+const struct mrb_data_type cg_rect_type = {
+ "CG_Rect", cg_free_rect };
+
+static mrb_value
+cg_cRect_initialize(mrb_state *mrb, mrb_value self)
+{
+ mrb_int x, y, width, height;
+ struct cg_rect *ptr;
+
+ mrb_get_args(mrb, "iiii", &x, &y, &width, &height);
+ ptr = (struct cg_rect *)DATA_PTR(self);
+ if(ptr) mrb_free(mrb, ptr);
+ ptr = (struct cg_rect *)mrb_malloc(mrb, sizeof(struct cg_rect));
+
+ ptr->rect.x = x;
+ ptr->rect.y = y;
+ ptr->rect.w = width;
+ ptr->rect.h = height;
+
+ mrb_data_init(self, ptr, &cg_rect_type);
+ return self;
+}
+
+static mrb_value
+cg_cRect_get_x(mrb_state *mrb, mrb_value self)
+{
+ struct cg_rect *ptr;
+
+ ptr = (struct cg_rect *)DATA_PTR(self);
+
+ return mrb_int_value(mrb, ptr->rect.x);
+}
+
+static mrb_value
+cg_cRect_set_x(mrb_state *mrb, mrb_value self)
+{
+ mrb_int x;
+ struct cg_rect *ptr;
+
+ mrb_get_args(mrb, "i", &x);
+ ptr = (struct cg_rect *)DATA_PTR(self);
+
+ ptr->rect.x = x;
+
+ return mrb_int_value(mrb, ptr->rect.x);
+}
+
+static mrb_value
+cg_cRect_get_y(mrb_state *mrb, mrb_value self)
+{
+ struct cg_rect *ptr;
+
+ ptr = (struct cg_rect *)DATA_PTR(self);
+
+ return mrb_int_value(mrb, ptr->rect.y);
+}
+
+static mrb_value
+cg_cRect_set_y(mrb_state *mrb, mrb_value self)
+{
+ mrb_int y;
+ struct cg_rect *ptr;
+
+ mrb_get_args(mrb, "i", &y);
+ ptr = (struct cg_rect *)DATA_PTR(self);
+
+ ptr->rect.y = y;
+
+ return mrb_int_value(mrb, ptr->rect.y);
+}
+
+static mrb_value
+cg_cRect_get_width(mrb_state *mrb, mrb_value self)
+{
+ struct cg_rect *ptr;
+
+ ptr = (struct cg_rect *)DATA_PTR(self);
+
+ return mrb_int_value(mrb, ptr->rect.w);
+}
+
+static mrb_value
+cg_cRect_set_width(mrb_state *mrb, mrb_value self)
+{
+ mrb_int width;
+ struct cg_rect *ptr;
+
+ mrb_get_args(mrb, "i", &width);
+ ptr = (struct cg_rect *)DATA_PTR(self);
+
+ ptr->rect.w = width;
+
+ return mrb_int_value(mrb, ptr->rect.w);
+}
+
+static mrb_value
+cg_cRect_get_height(mrb_state *mrb, mrb_value self)
+{
+ struct cg_rect *ptr;
+
+ ptr = (struct cg_rect *)DATA_PTR(self);
+
+ return mrb_int_value(mrb, ptr->rect.h);
+}
+
+static mrb_value
+cg_cRect_set_height(mrb_state *mrb, mrb_value self)
+{
+ mrb_int height;
+ struct cg_rect *ptr;
+
+ mrb_get_args(mrb, "i", &height);
+ ptr = (struct cg_rect *)DATA_PTR(self);
+
+ ptr->rect.h = height;
+
+ return mrb_int_value(mrb, ptr->rect.h);
+}
+
+static mrb_value
+cg_cRect_draw_lines(mrb_state *mrb, mrb_value self)
+{
+ struct cg_rect *ptr;
+ ptr = (struct cg_rect *)DATA_PTR(self);
+ SDL_RenderDrawRect(cg_core.renderer, &ptr->rect);
+
+ return self;
+}
+
+static mrb_value
+cg_cRect_draw_fill(mrb_state *mrb, mrb_value self)
+{
+ struct cg_rect *ptr;
+ ptr = (struct cg_rect *)DATA_PTR(self);
+ SDL_RenderFillRect(cg_core.renderer, &ptr->rect);
+
+ return self;
+}
+
+static mrb_value
+cg_cRect_collide(mrb_state *mrb, mrb_value self)
+{
+ struct cg_rect *ptr, *that;
+
+ mrb_get_args(mrb, "d", &that, &cg_rect_type);
+ ptr = (struct cg_rect *)DATA_PTR(self);
+
+ return mrb_bool_value(
+ ptr->rect.x <= that->rect.x + that->rect.w &&
+ ptr->rect.x + ptr->rect.w >= that->rect.x &&
+ ptr->rect.y <= that->rect.y + that->rect.h &&
+ ptr->rect.y + ptr->rect.h >= that->rect.y);
+}
+
+void
+cg_rect_init(mrb_state *mrb)
+{
+ struct RClass *cg_m, *cg_cRect;
+
+ cg_m = mrb_module_get(mrb, "CandyGear");
+ cg_cRect = mrb_define_class_under(mrb, cg_m, "Rect", mrb->object_class);
+ MRB_SET_INSTANCE_TT(cg_cRect, MRB_TT_DATA);
+ mrb_define_method(
+ mrb, cg_cRect, "initialize", cg_cRect_initialize, MRB_ARGS_REQ(4));
+ mrb_define_method(mrb, cg_cRect, "x", cg_cRect_get_x, MRB_ARGS_NONE());
+ mrb_define_method(mrb, cg_cRect, "x=", cg_cRect_set_x, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, cg_cRect, "y", cg_cRect_get_y, MRB_ARGS_NONE());
+ mrb_define_method(mrb, cg_cRect, "y=", cg_cRect_set_y, MRB_ARGS_REQ(1));
+ mrb_define_method(
+ mrb, cg_cRect, "width", cg_cRect_get_width, MRB_ARGS_NONE());
+ mrb_define_method(
+ mrb, cg_cRect, "width=", cg_cRect_set_width, MRB_ARGS_REQ(1));
+ mrb_define_method(
+ mrb, cg_cRect, "height", cg_cRect_get_height, MRB_ARGS_NONE());
+ mrb_define_method(
+ mrb, cg_cRect, "height=", cg_cRect_set_height, MRB_ARGS_REQ(1));
+ mrb_define_method(
+ mrb, cg_cRect, "draw_lines", cg_cRect_draw_lines, MRB_ARGS_NONE());
+ mrb_define_method(
+ mrb, cg_cRect, "draw_fill", cg_cRect_draw_fill, MRB_ARGS_NONE());
+ mrb_define_method(
+ mrb, cg_cRect, "collide?", cg_cRect_collide, MRB_ARGS_REQ(1));
+}
diff --git a/src/rect.h b/src/rect.h
new file mode 100644
index 0000000..64a7ba0
--- /dev/null
+++ b/src/rect.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CANDY_GEAR_RECT_H
+#define CANDY_GEAR_RECT_H 1
+
+#include "core.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct cg_rect
+{
+ SDL_Rect rect;
+};
+
+extern const struct mrb_data_type cg_rect_type;
+
+void
+cg_rect_init(mrb_state *mrb);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* CANDY_GEAR_RECT_H */
diff --git a/src/sound.c b/src/sound.c
new file mode 100644
index 0000000..8ca009c
--- /dev/null
+++ b/src/sound.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "sound.h"
+
+void
+cg_free_sound(mrb_state *mrb, void* obj)
+{
+ struct cg_sound *ptr = obj;
+
+ Mix_FreeChunk(ptr->chunk);
+ mrb_free(mrb, ptr);
+}
+
+const struct mrb_data_type cg_sound_type = {
+ "CG_Sound", cg_free_sound };
+
+static mrb_value
+cg_cSound_initialize(mrb_state *mrb, mrb_value self)
+{
+ const char *file_path;
+
+ struct cg_sound *ptr;
+
+ mrb_get_args(mrb, "z", &file_path);
+ ptr = (struct cg_sound *)DATA_PTR(self);
+ if(ptr) mrb_free(mrb, ptr);
+ ptr = (struct cg_sound *)mrb_malloc(mrb, sizeof(struct cg_sound));
+
+ ptr->chunk = Mix_LoadWAV(file_path);
+ if(ptr->chunk == NULL) mrb_raise(mrb, E_RUNTIME_ERROR, Mix_GetError());
+
+ mrb_data_init(self, ptr, &cg_sound_type);
+ return self;
+}
+
+static mrb_value
+cg_cSound_play(mrb_state *mrb, mrb_value self)
+{
+ struct cg_sound *ptr;
+
+ ptr = (struct cg_sound *)DATA_PTR(self);
+
+ Mix_PlayChannel(-1, ptr->chunk, 0);
+
+ return self;
+}
+
+void
+cg_sound_init(mrb_state *mrb)
+{
+ struct RClass *cg_m, *cg_cSound;
+
+ cg_m = mrb_module_get(mrb, "CandyGear");
+ cg_cSound = mrb_define_class_under(mrb, cg_m, "Sound", mrb->object_class);
+ MRB_SET_INSTANCE_TT(cg_cSound, MRB_TT_DATA);
+ mrb_define_method(
+ mrb, cg_cSound, "initialize", cg_cSound_initialize, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, cg_cSound, "play", cg_cSound_play, MRB_ARGS_NONE());
+}
diff --git a/src/sound.h b/src/sound.h
new file mode 100644
index 0000000..f2cd2e7
--- /dev/null
+++ b/src/sound.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CANDY_GEAR_SOUND_H
+#define CANDY_GEAR_SOUND_H 1
+
+#include "core.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct cg_sound
+{
+ Mix_Chunk *chunk;
+};
+
+extern const struct mrb_data_type cg_sound_type;
+
+void
+cg_sound_init(mrb_state *mrb);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* CANDY_GEAR_SOUND_H */
diff --git a/src/sprite.c b/src/sprite.c
new file mode 100644
index 0000000..b8495d4
--- /dev/null
+++ b/src/sprite.c
@@ -0,0 +1,198 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "sprite.h"
+#include "sprite_implementation.h"
+
+#include <mruby/hash.h>
+
+#include "point.h"
+#include "texture.h"
+
+// This variable works as a constant
+static mrb_sym id_at_texture;
+
+void
+cg_free_sprite(mrb_state *mrb, void* obj)
+{
+ struct cg_sprite *ptr = obj;
+
+ mrb_free(mrb, ptr);
+}
+
+const struct mrb_data_type cg_sprite_type = {
+ "CG_Sprite", cg_free_sprite };
+
+static mrb_value
+cg_cSrpite_load_yaml(mrb_state *mrb, mrb_value klass)
+{
+ const char *file_path;
+ struct cg_SpriteArray_s sprite_ary;
+ mrb_value hash, texture;
+
+ mrb_get_args(mrb, "zo", &file_path, &texture);
+
+ if(!cg_SpriteArray_constructor(&sprite_ary, file_path))
+ mrb_raise(mrb, E_RUNTIME_ERROR, "failed to open sprites");
+
+ hash = mrb_hash_new_capa(mrb, sprite_ary.len);
+
+ for(int i = 0; i < sprite_ary.len; i++)
+ {
+ mrb_hash_set(
+ mrb, hash,
+ mrb_symbol_value(mrb_intern_cstr(mrb, sprite_ary.sprites[i].name)),
+ mrb_funcall(
+ mrb, klass, "new", 5,
+ texture,
+ mrb_int_value(mrb, sprite_ary.sprites[i].x),
+ mrb_int_value(mrb, sprite_ary.sprites[i].y),
+ mrb_int_value(mrb, sprite_ary.sprites[i].width),
+ mrb_int_value(mrb, sprite_ary.sprites[i].height)));
+ }
+
+ cg_SpriteArray_destructor(&sprite_ary);
+
+ return hash;
+}
+
+static mrb_value
+cg_cSprite_initialize(mrb_state *mrb, mrb_value self)
+{
+ mrb_value texture_v;
+ mrb_int x, y, width, height;
+ struct cg_sprite *ptr;
+
+ mrb_get_args(mrb, "oiiii", &texture_v, &x, &y, &width, &height);
+ ptr = (struct cg_sprite *)DATA_PTR(self);
+ if(ptr) mrb_free(mrb, ptr);
+ ptr = (struct cg_sprite *)mrb_malloc(mrb, sizeof(struct cg_sprite));
+
+ mrb_iv_set(mrb, self, id_at_texture, texture_v);
+ ptr->rect.x = x;
+ ptr->rect.y = y;
+ ptr->rect.w = width;
+ ptr->rect.h = height;
+
+ mrb_data_init(self, ptr, &cg_sprite_type);
+ return self;
+}
+
+static mrb_value
+cg_cSprite_texture(mrb_state *mrb, mrb_value self)
+{
+ return mrb_iv_get(mrb, self, id_at_texture);
+}
+
+static mrb_value
+cg_cSprite_width(mrb_state *mrb, mrb_value self)
+{
+ struct cg_sprite *ptr;
+
+ ptr = (struct cg_sprite *)DATA_PTR(self);
+
+ return mrb_int_value(mrb, ptr->rect.w);
+}
+
+static mrb_value
+cg_cSprite_height(mrb_state *mrb, mrb_value self)
+{
+ struct cg_sprite *ptr;
+
+ ptr = (struct cg_sprite *)DATA_PTR(self);
+
+ return mrb_int_value(mrb, ptr->rect.h);
+}
+
+static mrb_value
+cg_cSprite_draw(mrb_state *mrb, mrb_value self)
+{
+ SDL_Rect dst_rect;
+
+ mrb_value texture_v;
+
+ struct cg_sprite *ptr;
+ struct cg_texture *texture_ptr;
+ struct cg_point *point;
+
+ mrb_get_args(mrb, "d", &point, &cg_point_type);
+
+ ptr = (struct cg_sprite *)DATA_PTR(self);
+ texture_v = mrb_iv_get(mrb, self, id_at_texture);
+ texture_ptr = (struct cg_texture *)DATA_PTR(texture_v);
+
+ dst_rect.x = point->data.x;
+ dst_rect.y = point->data.y;
+ dst_rect.w = ptr->rect.w;
+ dst_rect.h = ptr->rect.h;
+
+ SDL_RenderCopy(cg_core.renderer, texture_ptr->data, &ptr->rect, &dst_rect);
+
+ return self;
+}
+
+static mrb_value
+cg_cSprite_draw_xy(mrb_state *mrb, mrb_value self)
+{
+ SDL_Rect dst_rect;
+
+ mrb_value texture_v;
+
+ mrb_int x, y;
+ struct cg_sprite *ptr;
+ struct cg_texture *texture_ptr;
+
+ mrb_get_args(mrb, "ii", &x, &y);
+
+ ptr = (struct cg_sprite *)DATA_PTR(self);
+ texture_v = mrb_iv_get(mrb, self, id_at_texture);
+ texture_ptr = (struct cg_texture *)DATA_PTR(texture_v);
+
+ dst_rect.x = x;
+ dst_rect.y = y;
+ dst_rect.w = ptr->rect.w;
+ dst_rect.h = ptr->rect.h;
+
+ SDL_RenderCopy(cg_core.renderer, texture_ptr->data, &ptr->rect, &dst_rect);
+
+ return self;
+}
+
+void
+cg_sprite_init(mrb_state *mrb)
+{
+ struct RClass *cg_m, *cg_cSprite;
+
+ id_at_texture = mrb_intern_lit(mrb, "@texture");
+
+ cg_m = mrb_module_get(mrb, "CandyGear");
+ cg_cSprite = mrb_define_class_under(mrb, cg_m, "Sprite", mrb->object_class);
+ MRB_SET_INSTANCE_TT(cg_cSprite, MRB_TT_DATA);
+ mrb_define_class_method(
+ mrb, cg_cSprite, "load_yaml", cg_cSrpite_load_yaml, MRB_ARGS_REQ(2));
+ mrb_define_method(
+ mrb, cg_cSprite, "initialize", cg_cSprite_initialize, MRB_ARGS_REQ(5));
+ mrb_define_method(
+ mrb, cg_cSprite, "texture", cg_cSprite_texture, MRB_ARGS_NONE());
+ mrb_define_method(
+ mrb, cg_cSprite, "width", cg_cSprite_width, MRB_ARGS_NONE());
+ mrb_define_method(
+ mrb, cg_cSprite, "height", cg_cSprite_height, MRB_ARGS_NONE());
+ mrb_define_method(
+ mrb, cg_cSprite, "draw", cg_cSprite_draw, MRB_ARGS_REQ(1));
+ mrb_define_method(
+ mrb, cg_cSprite, "draw_xy", cg_cSprite_draw_xy, MRB_ARGS_REQ(2));
+}
diff --git a/src/sprite.h b/src/sprite.h
new file mode 100644
index 0000000..7a85418
--- /dev/null
+++ b/src/sprite.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CANDY_GEAR_SPRITE_H
+#define CANDY_GEAR_SPRITE_H 1
+
+#include "core.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct cg_sprite
+{
+ SDL_Rect rect;
+};
+
+extern const struct mrb_data_type cg_sprite_type;
+
+void
+cg_sprite_init(mrb_state *mrb);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* CANDY_GEAR_SPRITE_H */
diff --git a/src/sprite_implementation.cpp b/src/sprite_implementation.cpp
new file mode 100644
index 0000000..4ba0edf
--- /dev/null
+++ b/src/sprite_implementation.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "sprite_implementation.h"
+
+#include <fstream>
+
+#include <yaml-cpp/yaml.h>
+
+SDL_bool
+cg_SpriteArray_constructor(
+ struct cg_SpriteArray_s *self, const char *file_path)
+{
+ YAML::Node root = YAML::LoadFile(file_path);
+ self->len = root.size();
+ self->sprites = new cg_Sprite_s[self->len];
+
+ int index = 0;
+ for(YAML::const_iterator sprite_node = root.begin();
+ sprite_node != root.end();
+ sprite_node++)
+ {
+ cg_Sprite_s *sprite = &self->sprites[index];
+
+ auto node_name = sprite_node->first.as<std::string>();
+ sprite->name = new char[node_name.size() + 1];
+ strcpy(sprite->name, node_name.c_str());
+
+ sprite->x = sprite_node->second["x"].as<int32_t>();
+ sprite->y = sprite_node->second["y"].as<int32_t>();
+ sprite->width = sprite_node->second["width"].as<int32_t>();
+ sprite->height = sprite_node->second["height"].as<int32_t>();
+
+ index++;
+ }
+
+ return SDL_TRUE;
+}
+
+void
+cg_SpriteArray_destructor(struct cg_SpriteArray_s *self)
+{
+ for(auto i = 0; i < self->len; i++) delete[] self->sprites[i].name;
+ delete[] self->sprites;
+}
diff --git a/src/sprite_implementation.h b/src/sprite_implementation.h
new file mode 100644
index 0000000..c421188
--- /dev/null
+++ b/src/sprite_implementation.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CANDY_GEAR_SPRITE_IMPLEMENTATION_H
+#define CANDY_GEAR_SPRITE_IMPLEMENTATION_H 1
+
+#include <SDL2/SDL.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct cg_Sprite_s
+{
+ char *name;
+ int32_t x, y, width, height;
+};
+
+struct cg_SpriteArray_s
+{
+ int32_t len;
+ struct cg_Sprite_s *sprites;
+};
+
+SDL_bool
+cg_SpriteArray_constructor(
+ struct cg_SpriteArray_s *self, const char *file_path);
+
+void
+cg_SpriteArray_destructor(struct cg_SpriteArray_s *self);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* CANDY_GEAR_SPRITE_IMPLEMENTATION_H */
diff --git a/src/texture.c b/src/texture.c
new file mode 100644
index 0000000..f363c1f
--- /dev/null
+++ b/src/texture.c
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "texture.h"
+
+#include "color.h"
+#include "core.h"
+#include "font.h"
+#include "palette.h"
+#include "pgm_image.h"
+#include "point.h"
+
+void
+cg_free_texture(mrb_state *mrb, void* obj)
+{
+ struct cg_texture *ptr = obj;
+
+ SDL_DestroyTexture(ptr->data);
+ mrb_free(mrb, ptr);
+}
+
+const struct mrb_data_type cg_texture_type = {
+ "CG_Texture", cg_free_texture };
+
+static mrb_value
+texture_alloc(mrb_state *mrb, mrb_value class)
+{
+ struct RClass *cg_cTexture = mrb_class_ptr(class);
+ enum mrb_vtype ttype = MRB_INSTANCE_TT(cg_cTexture);
+
+ // I do not know if I need this. If things break, try uncomment this line.
+ // if (ttype == 0) ttype = MRB_TT_OBJECT;
+ return mrb_obj_value(
+ (struct RObject*)mrb_obj_alloc(mrb, ttype, cg_cTexture));
+}
+
+static mrb_value
+cg_cTexture_from_image(mrb_state *mrb, mrb_value self)
+{
+ PGMImage pgm_img;
+
+ struct mrb_value texture = texture_alloc(mrb, self);
+ const char *file_path;
+ struct cg_palette *palette;
+ struct cg_texture *ptr;
+ mrb_bool palette_given;
+ SDL_Surface *surface = NULL;
+
+ mrb_get_args(
+ mrb, "z|d?", &file_path, &palette, &cg_palette_type, &palette_given);
+ ptr = (struct cg_texture *)DATA_PTR(texture);
+ if(ptr) mrb_free(mrb, ptr);
+ ptr = (struct cg_texture *)mrb_malloc(mrb, sizeof(struct cg_texture));
+ if(palette_given)
+ {
+ if(!PGMImage_constructor(&pgm_img, file_path))
+ mrb_raise(mrb, E_RUNTIME_ERROR, pgm_img.error);
+
+ // Depth = 8 bits image.
+ // Pitch = 1 byte (8 bits) * image width.
+ surface = SDL_CreateRGBSurfaceWithFormatFrom(
+ pgm_img.data, pgm_img.width, pgm_img.height, 8, pgm_img.width,
+ SDL_PIXELFORMAT_INDEX8);
+ if(!surface) mrb_raise(mrb, E_RUNTIME_ERROR, SDL_GetError());
+ SDL_SetPaletteColors(surface->format->palette, palette->colors, 0, 256);
+ }
+ else
+ {
+ surface = IMG_Load(file_path);
+ if(!surface) mrb_raise(mrb, E_RUNTIME_ERROR, IMG_GetError());
+ }
+
+ ptr->data = SDL_CreateTextureFromSurface(cg_core.renderer, surface);
+ SDL_SetTextureBlendMode(ptr->data, SDL_BLENDMODE_BLEND);
+ SDL_FreeSurface(surface);
+ if(palette_given) PGMImage_destructor(&pgm_img);
+ if(ptr->data == NULL) mrb_raise(mrb, E_RUNTIME_ERROR, SDL_GetError());
+
+ SDL_QueryTexture(ptr->data, NULL, NULL, &ptr->width, &ptr->height);
+
+ mrb_data_init(texture, ptr, &cg_texture_type);
+ return texture;
+}
+
+mrb_value
+cg_cText_from_text(mrb_state *mrb, mrb_value self)
+{
+ char *text;
+ SDL_Surface *surface = NULL;
+ mrb_bool background;
+ struct mrb_value texture = texture_alloc(mrb, self);
+ struct cg_color *tx_color_ptr, *bg_color_ptr;
+ struct cg_font *font_ptr;
+ struct cg_texture *ptr;
+
+ mrb_get_args(
+ mrb, "zdd|d?", &text, &font_ptr, &cg_font_type,
+ &tx_color_ptr, &cg_color_type, &bg_color_ptr, &cg_color_type,
+ &background);
+
+ ptr = (struct cg_texture *)DATA_PTR(texture);
+ if(ptr) mrb_free(mrb, ptr);
+ ptr = (struct cg_texture *)mrb_malloc(mrb, sizeof(struct cg_texture));
+
+ // Create surface.
+ if(background)
+ surface = TTF_RenderUTF8_Shaded(
+ font_ptr->data, text, tx_color_ptr->data, bg_color_ptr->data);
+ else
+ surface = TTF_RenderUTF8_Solid(font_ptr->data, text, tx_color_ptr->data);
+ if(surface == NULL) mrb_raise(mrb, E_RUNTIME_ERROR, TTF_GetError());
+
+ // Convert the surface to a texture.
+ ptr->data = SDL_CreateTextureFromSurface(cg_core.renderer, surface);
+ SDL_FreeSurface(surface);
+ if(ptr->data == NULL) mrb_raise(mrb, E_RUNTIME_ERROR, SDL_GetError());
+
+ SDL_QueryTexture(ptr->data, NULL, NULL, &ptr->width, &ptr->height);
+ mrb_data_init(texture, ptr, &cg_texture_type);
+ return texture;
+}
+
+static mrb_value
+cg_cTexture_width(mrb_state *mrb, mrb_value self)
+{
+ struct cg_texture *ptr;
+
+ ptr = (struct cg_texture *)DATA_PTR(self);
+
+ return mrb_int_value(mrb, ptr->width);
+}
+
+static mrb_value
+cg_cTexture_height(mrb_state *mrb, mrb_value self)
+{
+ struct cg_texture *ptr;
+
+ ptr = (struct cg_texture *)DATA_PTR(self);
+
+ return mrb_int_value(mrb, ptr->height);
+}
+
+static mrb_value
+cg_cTexture_draw(mrb_state *mrb, mrb_value self)
+{
+ SDL_Rect dst_rect;
+
+ struct cg_texture *ptr;
+ struct cg_point *point;
+
+ mrb_get_args(mrb, "d", &point, &cg_point_type);
+ ptr = (struct cg_texture *)DATA_PTR(self);
+
+ dst_rect.x = point->data.x;
+ dst_rect.y = point->data.y;
+ dst_rect.w = ptr->width;
+ dst_rect.h = ptr->height;
+
+ SDL_RenderCopy(cg_core.renderer, ptr->data, NULL, &dst_rect);
+
+ return self;
+}
+
+static mrb_value
+cg_cTexture_draw_xy(mrb_state *mrb, mrb_value self)
+{
+ SDL_Rect dst_rect;
+
+ struct cg_texture *ptr;
+ mrb_int x, y;
+
+ mrb_get_args(mrb, "ii", &x, &y);
+ ptr = (struct cg_texture *)DATA_PTR(self);
+
+ dst_rect.x = x;
+ dst_rect.y = y;
+ dst_rect.w = ptr->width;
+ dst_rect.h = ptr->height;
+
+ SDL_RenderCopy(cg_core.renderer, ptr->data, NULL, &dst_rect);
+
+ return self;
+}
+
+void
+cg_texture_init(mrb_state *mrb)
+{
+ struct RClass *cg_m, *cg_cTexture;
+
+ cg_m = mrb_module_get(mrb, "CandyGear");
+ cg_cTexture = mrb_define_class_under(
+ mrb, cg_m, "Texture", mrb->object_class);
+ MRB_SET_INSTANCE_TT(cg_cTexture, MRB_TT_DATA);
+
+ mrb_undef_class_method(mrb, cg_cTexture, "new");
+ mrb_define_class_method(
+ mrb, cg_cTexture, "from_image", cg_cTexture_from_image,
+ MRB_ARGS_REQ(1) | MRB_ARGS_OPT(1));
+ mrb_define_class_method(
+ mrb, cg_cTexture, "from_text", cg_cText_from_text,
+ MRB_ARGS_REQ(3)|MRB_ARGS_OPT(1));
+ mrb_define_method(
+ mrb, cg_cTexture, "width", cg_cTexture_width, MRB_ARGS_NONE());
+ mrb_define_method(
+ mrb, cg_cTexture, "height", cg_cTexture_height, MRB_ARGS_NONE());
+ mrb_define_method(
+ mrb, cg_cTexture, "draw", cg_cTexture_draw, MRB_ARGS_REQ(1));
+ mrb_define_method(
+ mrb, cg_cTexture, "draw_xy", cg_cTexture_draw_xy, MRB_ARGS_REQ(2));
+}
diff --git a/src/texture.h b/src/texture.h
new file mode 100644
index 0000000..f89542f
--- /dev/null
+++ b/src/texture.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CANDY_GEAR_TEXTURE_H
+#define CANDY_GEAR_TEXTURE_H 1
+
+#include "core.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct cg_texture
+{
+ SDL_Texture *data;
+ int width, height;
+};
+
+extern const struct mrb_data_type cg_texture_type;
+
+void
+cg_texture_init(mrb_state *mrb);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* CANDY_GEAR_TEXTURE_H */